dvadf
File manager - Edit - /home/centroca/public_html/DebugEvents.tar
Back
EventsCollection.php 0000644 00000021364 15253207572 0010553 0 ustar 00 <?php namespace WPMailSMTP\Admin\DebugEvents; use WPMailSMTP\WP; /** * Debug Events Collection. * * @since 3.0.0 */ class EventsCollection implements \Countable, \Iterator { /** * Default number of log entries per page. * * @since 3.0.0 * * @var int */ const PER_PAGE = 10; /** * Number of log entries per page. * * @since 3.0.0 * * @var int */ public static $per_page; /** * List of all Event instances. * * @since 3.0.0 * * @var array */ private $list = []; /** * List of current collection instance parameters. * * @since 3.0.0 * * @var array */ private $params; /** * Used for \Iterator when iterating through Queue in loops. * * @since 3.0.0 * * @var int */ private $iterator_position = 0; /** * Collection constructor. * $events = new EventsCollection( [ 'type' => 0 ] ); * * @since 3.0.0 * * @param array $params The events collection parameters. */ public function __construct( array $params = [] ) { $this->set_per_page(); $this->params = $this->process_params( $params ); } /** * Set the per page attribute to the screen options value. * * @since 3.0.0 */ protected function set_per_page() { $per_page = (int) get_user_meta( get_current_user_id(), 'wp_mail_smtp_debug_events_per_page', true ); if ( $per_page < 1 ) { $per_page = self::PER_PAGE; } self::$per_page = $per_page; } /** * Verify, sanitize, and populate with default values * all the passed parameters, which participate in DB queries. * * @since 3.0.0 * * @param array $params The events collection parameters. * * @return array */ public function process_params( $params ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.MaxExceeded $params = (array) $params; $processed = []; /* * WHERE. */ // Single ID. if ( ! empty( $params['id'] ) ) { $processed['id'] = (int) $params['id']; } // Multiple IDs. if ( ! empty( $params['ids'] ) && is_array( $params['ids'] ) ) { $processed['ids'] = array_unique( array_filter( array_map( 'intval', array_values( $params['ids'] ) ) ) ); } // Type. if ( isset( $params['type'] ) && in_array( $params['type'], array_keys( Event::get_types() ), true ) ) { $processed['type'] = (int) $params['type']; } // Search. if ( ! empty( $params['search'] ) ) { $processed['search'] = sanitize_text_field( $params['search'] ); } /* * LIMIT. */ if ( ! empty( $params['offset'] ) ) { $processed['offset'] = (int) $params['offset']; } if ( ! empty( $params['per_page'] ) ) { $processed['per_page'] = (int) $params['per_page']; } /* * Sent date. */ if ( ! empty( $params['date'] ) ) { if ( is_string( $params['date'] ) ) { $params['date'] = array_fill( 0, 2, $params['date'] ); } elseif ( is_array( $params['date'] ) && count( $params['date'] ) === 1 ) { $params['date'] = array_fill( 0, 2, $params['date'][0] ); } // We pass array and treat it as a range from:to. if ( is_array( $params['date'] ) && count( $params['date'] ) === 2 ) { $date_start = WP::get_day_period_date( 'start_of_day', strtotime( $params['date'][0] ), 'Y-m-d H:i:s', true ); $date_end = WP::get_day_period_date( 'end_of_day', strtotime( $params['date'][1] ), 'Y-m-d H:i:s', true ); if ( ! empty( $date_start ) && ! empty( $date_end ) ) { $processed['date'] = [ $date_start, $date_end ]; } } } // Merge missing values with defaults. return wp_parse_args( $processed, $this->get_default_params() ); } /** * Get the list of default params for a usual query. * * @since 3.0.0 * * @return array */ protected function get_default_params() { return [ 'offset' => 0, 'per_page' => self::$per_page, 'order' => 'DESC', 'orderby' => 'id', 'search' => '', ]; } /** * Get the SQL-ready string of WHERE part for a query. * * @since 3.0.0 * * @return string */ private function build_where() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh global $wpdb; $where = [ '1=1' ]; // Shortcut single ID or multiple IDs. if ( ! empty( $this->params['id'] ) || ! empty( $this->params['ids'] ) ) { if ( ! empty( $this->params['id'] ) ) { $where[] = $wpdb->prepare( 'id = %d', $this->params['id'] ); } elseif ( ! empty( $this->params['ids'] ) ) { $where[] = 'id IN (' . implode( ',', $this->params['ids'] ) . ')'; } // When some ID(s) defined - we should ignore all other possible filtering options. return implode( ' AND ', $where ); } // Type. if ( isset( $this->params['type'] ) ) { $where[] = $wpdb->prepare( 'event_type = %d', $this->params['type'] ); } // Search. if ( ! empty( $this->params['search'] ) ) { $where[] = '(' . $wpdb->prepare( 'content LIKE %s', '%' . $wpdb->esc_like( $this->params['search'] ) . '%' ) . ' OR ' . $wpdb->prepare( 'initiator LIKE %s', '%' . $wpdb->esc_like( $this->params['search'] ) . '%' ) . ')'; } // Sent date. if ( ! empty( $this->params['date'] ) && is_array( $this->params['date'] ) && count( $this->params['date'] ) === 2 ) { $where[] = $wpdb->prepare( '( created_at >= %s AND created_at <= %s )', $this->params['date'][0], $this->params['date'][1] ); } return implode( ' AND ', $where ); } /** * Get the SQL-ready string of ORDER part for a query. * Order is always in the params, as per our defaults. * * @since 3.0.0 * * @return string */ private function build_order() { return 'ORDER BY ' . $this->params['orderby'] . ' ' . $this->params['order']; } /** * Get the SQL-ready string of LIMIT part for a query. * Limit is always in the params, as per our defaults. * * @since 3.0.0 * * @return string */ private function build_limit() { return 'LIMIT ' . $this->params['offset'] . ', ' . $this->params['per_page']; } /** * Count the number of DB records according to filters. * Do not retrieve actual records. * * @since 3.0.0 * * @return int */ public function get_count() { $table = DebugEvents::get_table_name(); $where = $this->build_where(); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared return (int) WP::wpdb()->get_var( "SELECT COUNT(id) FROM $table WHERE {$where}" ); // phpcs:enable } /** * Get the list of DB records. * You can either use array returned there OR iterate over the whole object, * as it implements Iterator interface. * * @since 3.0.0 * * @return EventsCollection */ public function get() { $table = DebugEvents::get_table_name(); $where = $this->build_where(); $limit = $this->build_limit(); $order = $this->build_order(); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared $data = WP::wpdb()->get_results( "SELECT * FROM $table WHERE {$where} {$order} {$limit}" ); // phpcs:enable if ( ! empty( $data ) ) { // As we got raw data we need to convert each row to Event. foreach ( $data as $row ) { $this->list[] = new Event( $row ); } } return $this; } /********************************************************************************************* * ****************************** \Counter interface method. ********************************* *********************************************************************************************/ /** * Count number of Record in a Queue. * * @since 3.0.0 * * @return int */ #[\ReturnTypeWillChange] public function count() { return count( $this->list ); } /********************************************************************************************* * ****************************** \Iterator interface methods. ******************************* *********************************************************************************************/ /** * Rewind the Iterator to the first element. * * @since 3.0.0 */ #[\ReturnTypeWillChange] public function rewind() { $this->iterator_position = 0; } /** * Return the current element. * * @since 3.0.0 * * @return Event|null Return null when no items in collection. */ #[\ReturnTypeWillChange] public function current() { return $this->valid() ? $this->list[ $this->iterator_position ] : null; } /** * Return the key of the current element. * * @since 3.0.0 * * @return int */ #[\ReturnTypeWillChange] public function key() { return $this->iterator_position; } /** * Move forward to next element. * * @since 3.0.0 */ #[\ReturnTypeWillChange] public function next() { ++ $this->iterator_position; } /** * Checks if current position is valid. * * @since 3.0.0 * * @return bool */ #[\ReturnTypeWillChange] public function valid() { return isset( $this->list[ $this->iterator_position ] ); } } Table.php 0000644 00000033666 15253207572 0006332 0 ustar 00 <?php namespace WPMailSMTP\Admin\DebugEvents; use WPMailSMTP\Helpers\Helpers; if ( ! class_exists( 'WP_List_Table', false ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } /** * Class Table that displays the list of debug events. * * @since 3.0.0 */ class Table extends \WP_List_Table { /** * Number of debug events by different types. * * @since 3.0.0 * * @var array */ public $counts; /** * Set up a constructor that references the parent constructor. * Using the parent reference to set some default configs. * * @since 3.0.0 */ public function __construct() { // Set parent defaults. parent::__construct( [ 'singular' => 'event', 'plural' => 'events', 'ajax' => false, ] ); // Include polyfill if mbstring PHP extension is not enabled. if ( ! function_exists( 'mb_substr' ) || ! function_exists( 'mb_strlen' ) ) { Helpers::include_mbstring_polyfill(); } } /** * Get the debug event types for filtering purpose. * * @since 3.0.0 * * @return array Associative array of debug event types StatusCode=>Name. */ public function get_types() { return Event::get_types(); } /** * Get the items counts for various types of debug logs. * * @since 3.0.0 */ public function get_counts() { $this->counts = []; // Base params with applied filters. $base_params = $this->get_filters_query_params(); $total_params = $base_params; unset( $total_params['type'] ); $this->counts['total'] = ( new EventsCollection( $total_params ) )->get_count(); foreach ( $this->get_types() as $type => $name ) { $collection = new EventsCollection( array_merge( $base_params, [ 'type' => $type ] ) ); $this->counts[ 'type_' . $type ] = $collection->get_count(); } /** * Filters items counts by various types of debug events. * * @since 3.0.0 * * @param array $counts { * Items counts by types. * * @type integer $total Total items count. * @type integer $status_{$type_key} Items count by type. * } */ $this->counts = apply_filters( 'wp_mail_smtp_admin_debug_events_table_get_counts', $this->counts ); } /** * Retrieve the view types. * * @since 3.0.0 */ public function get_views() { $base_url = $this->get_filters_base_url(); $current_type = $this->get_filtered_types(); $views = []; $views['all'] = sprintf( '<a href="%1$s" %2$s>%3$s <span class="count">(%4$d)</span></a>', esc_url( remove_query_arg( 'type', $base_url ) ), $current_type === false ? 'class="current"' : '', esc_html__( 'All', 'wp-mail-smtp' ), intval( $this->counts['total'] ) ); foreach ( $this->get_types() as $type => $type_label ) { $count = intval( $this->counts[ 'type_' . $type ] ); // Skipping types with no events. if ( $count === 0 && $current_type !== $type ) { continue; } $views[ $type ] = sprintf( '<a href="%1$s" %2$s>%3$s <span class="count">(%4$d)</span></a>', esc_url( add_query_arg( 'type', $type, $base_url ) ), $current_type === $type ? 'class="current"' : '', esc_html( $type_label ), $count ); } /** * Filters debug event item views. * * @since 3.0.0 * * @param array $views { * Debug event items views by types. * * @type string $all Total items view. * @type integer $status_key Items views by type. * } * @param array $counts { * Items counts by types. * * @type integer $total Total items count. * @type integer $status_{$status_key} Items count by types. * } */ return apply_filters( 'wp_mail_smtp_admin_debug_events_table_get_views', $views, $this->counts ); } /** * Define the table columns. * * @since 3.0.0 * * @return array Associative array of slug=>Name columns data. */ public function get_columns() { return [ 'event' => esc_html__( 'Event', 'wp-mail-smtp' ), 'type' => esc_html__( 'Type', 'wp-mail-smtp' ), 'content' => esc_html__( 'Content', 'wp-mail-smtp' ), 'initiator' => esc_html__( 'Source', 'wp-mail-smtp' ), 'created_at' => esc_html__( 'Date', 'wp-mail-smtp' ), ]; } /** * Display the main event title with a link to open event details. * * @since 3.0.0 * * @param Event $item Event object. * * @return string */ public function column_event( $item ) { return '<strong>' . '<a href="#" data-event-id="' . esc_attr( $item->get_id() ) . '"' . ' class="js-wp-mail-smtp-debug-event-preview row-title event-preview" title="' . esc_attr( $item->get_title() ) . '">' . esc_html( $item->get_title() ) . '</a>' . '</strong>'; } /** * Display event's type. * * @since 3.0.0 * * @param Event $item Event object. * * @return string */ public function column_type( $item ) { return esc_html( $item->get_type_name() ); } /** * Display event's content. * * @since 3.0.0 * * @param Event $item Event object. * * @return string */ public function column_content( $item ) { $content = $item->get_content(); if ( mb_strlen( $content ) > 100 ) { $content = mb_substr( $content, 0, 100 ) . '...'; } return wp_kses_post( $content ); } /** * Display event's wp_mail initiator. * * @since 3.0.0 * * @param Event $item Event object. * * @return string */ public function column_initiator( $item ) { return esc_html( $item->get_initiator() ); } /** * Display event's created date. * * @since 3.0.0 * * @param Event $item Event object. * * @return string */ public function column_created_at( $item ) { return $item->get_created_at_formatted(); } /** * Return type filter value or FALSE. * * @since 3.0.0 * * @return bool|integer */ public function get_filtered_types() { if ( ! isset( $_REQUEST['type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return false; } return intval( $_REQUEST['type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } /** * Return date filter value or FALSE. * * @since 3.0.0 * * @return bool|array */ public function get_filtered_dates() { if ( empty( $_REQUEST['date'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return false; } $dates = (array) explode( ' - ', sanitize_text_field( wp_unslash( $_REQUEST['date'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended return array_map( 'sanitize_text_field', $dates ); } /** * Return search filter values or FALSE. * * @since 3.0.0 * * @return bool|array */ public function get_filtered_search() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( empty( $_REQUEST['search'] ) ) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended return sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ); } /** * Whether the event log is filtered or not. * * @since 3.0.0 * * @return bool */ public function is_filtered() { $is_filtered = false; if ( $this->get_filtered_search() !== false || $this->get_filtered_dates() !== false || $this->get_filtered_types() !== false ) { $is_filtered = true; } return $is_filtered; } /** * Get current filters query parameters. * * @since 3.0.0 * * @return array */ public function get_filters_query_params() { $params = [ 'search' => $this->get_filtered_search(), 'type' => $this->get_filtered_types(), 'date' => $this->get_filtered_dates(), ]; return array_filter( $params, function ( $v ) { return $v !== false; } ); } /** * Get current filters base url. * * @since 3.0.0 * * @return string */ public function get_filters_base_url() { $base_url = DebugEvents::get_page_url(); $filters_params = $this->get_filters_query_params(); if ( isset( $filters_params['search'] ) ) { $base_url = add_query_arg( 'search', $filters_params['search'], $base_url ); } if ( isset( $filters_params['type'] ) ) { $base_url = add_query_arg( 'type', $filters_params['type'], $base_url ); } if ( isset( $filters_params['date'] ) ) { $base_url = add_query_arg( 'date', implode( ' - ', $filters_params['date'] ), $base_url ); } return $base_url; } /** * Get the data, prepare pagination, process bulk actions. * Prepare columns for display. * * @since 3.0.0 */ public function prepare_items() { // Retrieve count. $this->get_counts(); // Prepare all the params to pass to our Collection. All sanitization is done in that class. $params = $this->get_filters_query_params(); // Total amount for pagination with WHERE clause - super quick count DB request. $total_items = ( new EventsCollection( $params ) )->get_count(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_REQUEST['orderby'] ) && in_array( $_REQUEST['orderby'], [ 'event', 'type', 'content', 'initiator', 'created_at' ], true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $params['orderby'] = sanitize_key( $_REQUEST['orderby'] ); } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_REQUEST['order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $params['order'] = strtoupper( sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ) ) === 'DESC' ? 'DESC' : 'ASC'; } $params['offset'] = ( $this->get_pagenum() - 1 ) * EventsCollection::$per_page; // Get the data from the DB using parameters defined above. $collection = new EventsCollection( $params ); $this->items = $collection->get(); /* * Register our pagination options & calculations. */ $this->set_pagination_args( [ 'total_items' => $total_items, 'per_page' => EventsCollection::$per_page, ] ); } /** * Display the search box. * * @since 1.7.0 * * @param string $text The 'submit' button label. * @param string $input_id ID attribute value for the search input field. */ public function search_box( $text, $input_id ) { if ( ! $this->is_filtered() && ! $this->has_items() ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $search = ! empty( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_REQUEST['orderby'] ) && in_array( $_REQUEST['orderby'], [ 'event', 'type', 'content', 'initiator', 'created_at' ], true ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order_by = sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ); echo '<input type="hidden" name="orderby" value="' . esc_attr( $order_by ) . '" />'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! empty( $_REQUEST['order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $order = strtoupper( sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ) ) === 'DESC' ? 'DESC' : 'ASC'; echo '<input type="hidden" name="order" value="' . esc_attr( $order ) . '" />'; } ?> <p class="search-box"> <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_html( $text ); ?>:</label> <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="search" value="<?php echo esc_attr( $search ); ?>" /> <?php submit_button( $text, '', '', false, [ 'id' => 'search-submit' ] ); ?> </p> <?php } /** * Whether the table has items to display or not. * * @since 3.0.0 * * @return bool */ public function has_items() { return count( $this->items ) > 0; } /** * Message to be displayed when there are no items. * * @since 3.0.0 */ public function no_items() { if ( $this->is_filtered() ) { esc_html_e( 'No events found.', 'wp-mail-smtp' ); } else { esc_html_e( 'No events have been logged for now.', 'wp-mail-smtp' ); } } /** * Displays the table. * * @since 3.0.0 */ public function display() { $this->_column_headers = [ $this->get_columns(), [], [] ]; parent::display(); } /** * Hide the tablenav if there are no items in the table. * And remove the bulk action nonce and code. * * @since 3.0.0 * * @param string $which Which tablenav: top or bottom. */ protected function display_tablenav( $which ) { if ( ! $this->has_items() ) { return; } ?> <div class="tablenav <?php echo esc_attr( $which ); ?>"> <?php $this->extra_tablenav( $which ); $this->pagination( $which ); ?> <br class="clear" /> </div> <?php } /** * Extra controls to be displayed between bulk actions and pagination. * * @since 3.0.0 * * @param string $which Which tablenav: top or bottom. */ protected function extra_tablenav( $which ) { if ( $which !== 'top' || ! $this->has_items() ) { return; } $date = $this->get_filtered_dates() !== false ? implode( ' - ', $this->get_filtered_dates() ) : ''; ?> <div class="alignleft actions wp-mail-smtp-filter-date"> <input type="text" name="date" class="regular-text wp-mail-smtp-filter-date-selector wp-mail-smtp-filter-date__control" placeholder="<?php esc_attr_e( 'Select a date range', 'wp-mail-smtp' ); ?>" value="<?php echo esc_attr( $date ); ?>"> <button type="submit" name="action" value="filter_date" class="button wp-mail-smtp-filter-date__btn"> <?php esc_html_e( 'Filter', 'wp-mail-smtp' ); ?> </button> </div> <?php if ( current_user_can( wp_mail_smtp()->get_capability_manage_options() ) ) { wp_nonce_field( 'wp_mail_smtp_debug_events', 'wp-mail-smtp-debug-events-nonce', false ); printf( '<button id="wp-mail-smtp-delete-all-debug-events-button" type="button" class="button">%s</button>', esc_html__( 'Delete All Events', 'wp-mail-smtp' ) ); } } /** * Get the name of the primary column. * Important for the mobile view. * * @since 3.0.0 * * @return string The name of the primary column. */ protected function get_primary_column_name() { return 'event'; } } Event.php 0000644 00000027672 15253207572 0006364 0 ustar 00 <?php namespace WPMailSMTP\Admin\DebugEvents; use WPMailSMTP\WP; /** * Debug Event class. * * @since 3.0.0 */ class Event { /** * This is an error event. * * @since 3.0.0 */ const TYPE_ERROR = 0; /** * This is a debug event. * * @since 3.0.0 */ const TYPE_DEBUG = 1; /** * The event's ID. * * @since 3.0.0 * * @var int */ protected $id = 0; /** * The event's content. * * @since 3.0.0 * * @var string */ protected $content = ''; /** * The event's initiator - who called the `wp_mail` function? * JSON encoded string. * * @since 3.0.0 * * @var string */ protected $initiator = ''; /** * The event's type. * * @since 3.0.0 * * @var int */ protected $event_type = 0; /** * The date and time when this event was created. * * @since 3.0.0 * * @var \DateTime */ protected $created_at; /** * Retrieve a particular event when constructing the object. * * @since 3.0.0 * * @param int|object $id_or_row The event ID or object with event attributes. */ public function __construct( $id_or_row = null ) { $this->populate_event( $id_or_row ); } /** * Get and prepare the event data. * * @since 3.0.0 * * @param int|object $id_or_row The event ID or object with event attributes. */ private function populate_event( $id_or_row ) { $event = null; if ( is_numeric( $id_or_row ) ) { // Get by ID. $collection = new EventsCollection( [ 'id' => (int) $id_or_row ] ); $events = $collection->get(); if ( $events->valid() ) { $event = $events->current(); } } elseif ( is_object( $id_or_row ) && isset( $id_or_row->id, $id_or_row->content, $id_or_row->initiator, $id_or_row->event_type, $id_or_row->created_at ) ) { $event = $id_or_row; } if ( $event !== null ) { foreach ( get_object_vars( $event ) as $key => $value ) { $this->{$key} = $value; } } } /** * Event ID as per our DB table. * * @since 3.0.0 * * @return int */ public function get_id() { return (int) $this->id; } /** * Get the event title. * * @since 3.0.0 * * @return string */ public function get_title() { /* translators: %d the event ID. */ return sprintf( esc_html__( 'Event #%d', 'wp-mail-smtp' ), $this->get_id() ); } /** * Get the content of the event. * * @since 3.0.0 * * @return string */ public function get_content() { return $this->content; } /** * Get the event's type. * * @since 3.0.0 * * @return int */ public function get_type() { return (int) $this->event_type; } /** * Get the list of all event types. * * @since 3.0.0 * * @return array */ public static function get_types() { return [ self::TYPE_ERROR => esc_html__( 'Error', 'wp-mail-smtp' ), self::TYPE_DEBUG => esc_html__( 'Debug', 'wp-mail-smtp' ), ]; } /** * Get human readable type name. * * @since 3.0.0 * * @return string */ public function get_type_name() { $types = self::get_types(); return isset( $types[ $this->get_type() ] ) ? $types[ $this->get_type() ] : ''; } /** * Get the date/time when this event was created. * * @since 3.0.0 * * @throws \Exception Emits exception on incorrect date. * * @return \DateTime */ public function get_created_at() { $timezone = new \DateTimeZone( 'UTC' ); $date = false; if ( ! empty( $this->created_at ) ) { $date = \DateTime::createFromFormat( WP::datetime_mysql_format(), $this->created_at, $timezone ); } if ( $date === false ) { $date = new \DateTime( 'now', $timezone ); } return $date; } /** * Get the date/time when this event was created in a nicely formatted string. * * @since 3.0.0 * * @return string */ public function get_created_at_formatted() { try { $date = $this->get_created_at(); } catch ( \Exception $e ) { $date = null; } if ( empty( $date ) ) { return esc_html__( 'N/A', 'wp-mail-smtp' ); } return esc_html( date_i18n( WP::datetime_format(), strtotime( get_date_from_gmt( $date->format( WP::datetime_mysql_format() ) ) ) ) ); } /** * Get the event's initiator raw data. * Who called the `wp_mail` function? * * @since 3.0.0 * * @return array */ public function get_initiator_raw() { return json_decode( $this->initiator, true ); } /** * Get the event's initiator name. * Which plugin/theme (or WP core) called the `wp_mail` function? * * @since 3.0.0 * * @return string */ public function get_initiator() { $initiator = (array) $this->get_initiator_raw(); if ( empty( $initiator['file'] ) ) { return ''; } return WP::get_initiator_name( $initiator['file'] ); } /** * Get the event's initiator file path. * * @since 3.0.0 * * @return string */ public function get_initiator_file_path() { $initiator = (array) $this->get_initiator_raw(); if ( empty( $initiator['file'] ) ) { return ''; } return $initiator['file']; } /** * Get the event's initiator file line. * * @since 3.0.0 * * @return string */ public function get_initiator_file_line() { $initiator = (array) $this->get_initiator_raw(); if ( empty( $initiator['line'] ) ) { return ''; } return $initiator['line']; } /** * Get the event's initiator backtrace. * * @since 3.6.0 * * @return array */ private function get_initiator_backtrace() { $initiator = (array) $this->get_initiator_raw(); if ( empty( $initiator['backtrace'] ) ) { return []; } return $initiator['backtrace']; } /** * Get the event preview HTML. * * @since 3.0.0 * * @return string */ public function get_details_html() { $initiator = $this->get_initiator(); $initiator_backtrace = $this->get_initiator_backtrace(); ob_start(); ?> <div class="wp-mail-smtp-debug-event-preview"> <div class="wp-mail-smtp-debug-event-preview-subtitle"> <span><?php esc_html_e( 'Debug Event Details', 'wp-mail-smtp' ); ?></span> </div> <div class="wp-mail-smtp-debug-event-row wp-mail-smtp-debug-event-preview-type"> <span class="debug-event-label"><?php esc_html_e( 'Type', 'wp-mail-smtp' ); ?></span> <span class="debug-event-value"><?php echo esc_html( $this->get_type_name() ); ?></span> </div> <div class="wp-mail-smtp-debug-event-row wp-mail-smtp-debug-event-preview-date"> <span class="debug-event-label"><?php esc_html_e( 'Date', 'wp-mail-smtp' ); ?></span> <span class="debug-event-value"><?php echo esc_html( $this->get_created_at_formatted() ); ?></span> </div> <div class="wp-mail-smtp-debug-event-row wp-mail-smtp-debug-event-preview-content"> <span class="debug-event-label"><?php esc_html_e( 'Content', 'wp-mail-smtp' ); ?></span> <div class="debug-event-value"> <?php echo wp_kses( str_replace( [ "\r\n", "\r", "\n" ], '<br>', $this->get_content() ), [ 'br' => [] ] ); ?> </div> </div> <?php if ( ! empty( $initiator ) ) : ?> <div class="wp-mail-smtp-debug-event-row wp-mail-smtp-debug-event-preview-caller"> <span class="debug-event-label"><?php esc_html_e( 'Source', 'wp-mail-smtp' ); ?></span> <div class="debug-event-value"> <span class="debug-event-initiator"><?php echo esc_html( $initiator ); ?></span> <p class="debug-event-code"> <?php printf( /* Translators: %1$s the path of a file, %2$s the line number in the file. */ esc_html__( '%1$s (line: %2$s)', 'wp-mail-smtp' ), esc_html( $this->get_initiator_file_path() ), esc_html( $this->get_initiator_file_line() ) ); ?> <?php if ( ! empty( $initiator_backtrace ) ) : ?> <br><br> <b><?php esc_html_e( 'Backtrace:', 'wp-mail-smtp' ); ?></b> <br> <?php foreach ( $initiator_backtrace as $i => $item ) { printf( /* translators: %1$d - index number; %2$s - function name; %3$s - file path; %4$s - line number. */ esc_html__( '[%1$d] %2$s called at [%3$s:%4$s]', 'wp-mail-smtp' ), $i, isset( $item['class'] ) ? esc_html( $item['class'] . $item['type'] . $item['function'] ) : esc_html( $item['function'] ), isset( $item['file'] ) ? esc_html( $item['file'] ) : '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped isset( $item['line'] ) ? esc_html( $item['line'] ) : '' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); echo '<br>'; } ?> <?php endif; ?> </p> </div> </div> <?php endif; ?> </div> <?php return ob_get_clean(); } /** * Get the short details about this event (event content and the initiator's name). * * @since 3.0.0 * * @return string */ public function get_short_details() { $result = []; if ( ! empty( $this->get_initiator() ) ) { $result[] = sprintf( /* Translators: %s - Email initiator/source name. */ esc_html__( 'Email Source: %s', 'wp-mail-smtp' ), esc_html( $this->get_initiator() ) ); } $result[] = esc_html( $this->get_content() ); return implode( WP::EOL, $result ); } /** * Save a new or modified event in DB. * * @since 3.0.0 * * @throws \Exception When event init fails. * * @return Event New or updated event class instance. */ public function save() { global $wpdb; $table = DebugEvents::get_table_name(); if ( (bool) $this->get_id() ) { // Update the existing DB table record. $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching $table, [ 'content' => $this->content, 'initiator' => $this->initiator, 'event_type' => $this->event_type, 'created_at' => $this->get_created_at()->format( WP::datetime_mysql_format() ), ], [ 'id' => $this->get_id(), ], [ '%s', // content. '%s', // initiator. '%s', // type. '%s', // created_at. ], [ '%d', ] ); $event_id = $this->get_id(); } else { // Create a new DB table record. $wpdb->insert( $table, [ 'content' => $this->content, 'initiator' => $this->initiator, 'event_type' => $this->event_type, 'created_at' => $this->get_created_at()->format( WP::datetime_mysql_format() ), ], [ '%s', // content. '%s', // initiator. '%s', // type. '%s', // created_at. ] ); $event_id = $wpdb->insert_id; } try { $event = new Event( $event_id ); } catch ( \Exception $e ) { $event = new Event(); } return $event; } /** * Set the content of this event. * * @since 3.0.0 * * @param string|array $content The event's content. */ public function set_content( $content ) { if ( ! is_string( $content ) ) { $this->content = wp_json_encode( $content ); } else { $this->content = wp_strip_all_tags( str_replace( '<br>', "\r\n", $content ), false ); } } /** * Set the initiator by checking the backtrace for the wp_mail function call. * * @since 3.0.0 */ public function set_initiator() { $initiator = wp_mail_smtp()->get_wp_mail_initiator(); if ( empty( $initiator->get_file() ) ) { return; } $data['file'] = $initiator->get_file(); if ( ! empty( $initiator->get_line() ) ) { $data['line'] = $initiator->get_line(); } if ( DebugEvents::is_debug_enabled() ) { $data['backtrace'] = $initiator->get_backtrace(); } $this->initiator = wp_json_encode( $data ); } /** * Set the type of this event. * * @since 3.0.0 * * @param int $type The event's type. */ public function set_type( $type ) { $this->event_type = (int) $type; } /** * Whether the event instance is a valid entity to work with. * * @since 3.0.0 */ public function is_valid() { return ! ( empty( $this->id ) || empty( $this->created_at ) ); } /** * Whether this is an error event. * * @since 3.0.0 * * @return bool */ public function is_error() { return self::TYPE_ERROR === $this->get_type(); } /** * Whether this is a debug event. * * @since 3.0.0 * * @return bool */ public function is_debug() { return self::TYPE_DEBUG === $this->get_type(); } } DebugEvents.php 0000644 00000030306 15253207572 0007502 0 ustar 00 <?php namespace WPMailSMTP\Admin\DebugEvents; use WP_Error; use WPMailSMTP\Admin\Area; use WPMailSMTP\Options; use WPMailSMTP\Tasks\DebugEventsCleanupTask; use WPMailSMTP\WP; /** * Debug Events class. * * @since 3.0.0 */ class DebugEvents { /** * Transient name for the error debug events. * * @since 3.9.0 * * @var string */ const ERROR_DEBUG_EVENTS_TRANSIENT = 'wp_mail_smtp_error_debug_events_transient'; /** * Register hooks. * * @since 3.0.0 */ public function hooks() { // Process AJAX requests. add_action( 'wp_ajax_wp_mail_smtp_debug_event_preview', [ $this, 'process_ajax_debug_event_preview' ] ); add_action( 'wp_ajax_wp_mail_smtp_delete_all_debug_events', [ $this, 'process_ajax_delete_all_debug_events' ] ); // Initialize screen options for the Debug Events page. add_action( 'load-wp-mail-smtp_page_wp-mail-smtp-tools', [ $this, 'screen_options' ] ); add_filter( 'set-screen-option', [ $this, 'set_screen_options' ], 10, 3 ); add_filter( 'set_screen_option_wp_mail_smtp_debug_events_per_page', [ $this, 'set_screen_options' ], 10, 3 ); // Cancel previous debug events cleanup task if retention period option was changed. add_filter( 'wp_mail_smtp_options_set', [ $this, 'maybe_cancel_debug_events_cleanup_task' ] ); // Detect debug events log retention period constant change. if ( Options::init()->is_const_defined( 'debug_events', 'retention_period' ) ) { add_action( 'admin_init', [ $this, 'detect_debug_events_retention_period_constant_change' ] ); } } /** * Detect debug events retention period constant change. * * @since 3.6.0 */ public function detect_debug_events_retention_period_constant_change() { if ( ! WP::in_wp_admin() ) { return; } if ( Options::init()->is_const_changed( 'debug_events', 'retention_period' ) ) { ( new DebugEventsCleanupTask() )->cancel(); } } /** * Cancel previous debug events cleanup task if retention period option was changed. * * @since 3.6.0 * * @param array $options Currently processed options passed to a filter hook. * * @return array */ public function maybe_cancel_debug_events_cleanup_task( $options ) { if ( isset( $options['debug_events']['retention_period'] ) ) { // If this option has changed, cancel the recurring cleanup task and init again. if ( Options::init()->is_option_changed( $options['debug_events']['retention_period'], 'debug_events', 'retention_period' ) ) { ( new DebugEventsCleanupTask() )->cancel(); } } return $options; } /** * Process AJAX request for deleting all debug event entries. * * @since 3.0.0 */ public function process_ajax_delete_all_debug_events() { if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'wp_mail_smtp_debug_events' ) ) { wp_send_json_error( esc_html__( 'Access rejected.', 'wp-mail-smtp' ) ); } if ( ! current_user_can( wp_mail_smtp()->get_capability_manage_options() ) ) { wp_send_json_error( esc_html__( 'You don\'t have the capability to perform this action.', 'wp-mail-smtp' ) ); } if ( ! self::is_valid_db() ) { wp_send_json_error( esc_html__( 'For some reason the database table was not installed correctly. Please contact plugin support team to diagnose and fix the issue.', 'wp-mail-smtp' ) ); } global $wpdb; $table = self::get_table_name(); $sql = "TRUNCATE TABLE `$table`;"; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $result = $wpdb->query( $sql ); if ( $result !== false ) { wp_send_json_success( esc_html__( 'All debug event entries were deleted successfully.', 'wp-mail-smtp' ) ); } wp_send_json_error( sprintf( /* translators: %s - WPDB error message. */ esc_html__( 'There was an issue while trying to delete all debug event entries. Error message: %s', 'wp-mail-smtp' ), $wpdb->last_error ) ); } /** * Process AJAX request for debug event preview. * * @since 3.0.0 */ public function process_ajax_debug_event_preview() { if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'wp_mail_smtp_debug_events' ) ) { wp_send_json_error( esc_html__( 'Access rejected.', 'wp-mail-smtp' ) ); } if ( ! current_user_can( wp_mail_smtp()->get_capability_manage_options() ) ) { wp_send_json_error( esc_html__( 'You don\'t have the capability to perform this action.', 'wp-mail-smtp' ) ); } if ( ! self::is_valid_db() ) { wp_send_json_error( esc_html__( 'For some reason the database table was not installed correctly. Please contact plugin support team to diagnose and fix the issue.', 'wp-mail-smtp' ) ); } $event_id = isset( $_POST['id'] ) ? intval( $_POST['id'] ) : false; if ( empty( $event_id ) ) { wp_send_json_error( esc_html__( 'No Debug Event ID provided!', 'wp-mail-smtp' ) ); } $event = new Event( $event_id ); wp_send_json_success( [ 'title' => $event->get_title(), 'content' => $event->get_details_html(), ] ); } /** * Add the debug event to the DB. * * @since 3.0.0 * * @param string $message The event's message. * @param int $type The event's type. * * @return bool|int */ public static function add( $message = '', $type = 0 ) { if ( ! self::is_valid_db() ) { return false; } if ( ! in_array( $type, array_keys( Event::get_types() ), true ) ) { return false; } if ( $type === Event::TYPE_DEBUG && ! self::is_debug_enabled() ) { return false; } try { $event = new Event(); $event->set_type( $type ); $event->set_content( $message ); $event->set_initiator(); return $event->save()->get_id(); } catch ( \Exception $exception ) { return false; } } /** * Save the debug message. * * @since 3.0.0 * @since 3.5.0 Returns Event ID. * * @param string $message The debug message. * * @return bool|int */ public static function add_debug( $message = '' ) { return self::add( $message, Event::TYPE_DEBUG ); } /** * Add a debug event subject to a throttle window — only logs if no other event * with the same throttle key has been logged within the TTL. * * Useful for background errors that can fire on every page load (e.g. expired * OAuth token refresh failures) to avoid flooding the events table. * * @since 4.9.0 * * @param string $message The event's message. * @param string $throttle_key Unique key identifying this event's throttle bucket. * Automatically prefixed with `wp_mail_smtp_` before * being used as a transient key. * @param int $ttl Throttle window in seconds. Default 5 minutes. * @param int $type The event's type. Default Event::TYPE_ERROR (0). * * @return bool|int Event ID on success, false if throttled or save failed. */ public static function add_throttled( $message, $throttle_key, $ttl = 5 * MINUTE_IN_SECONDS, $type = 0 ) { $transient_key = 'wp_mail_smtp_' . $throttle_key; if ( get_transient( $transient_key ) ) { return false; } $event_id = self::add( $message, $type ); if ( $event_id !== false ) { set_transient( $transient_key, time(), $ttl ); } return $event_id; } /** * Get the debug message from the provided debug event IDs. * * @since 3.0.0 * * @param array|string|int $ids A single or a list of debug event IDs. * * @return array */ public static function get_debug_messages( $ids ) { global $wpdb; if ( empty( $ids ) ) { return []; } if ( ! self::is_valid_db() ) { return []; } // Convert to a string. if ( is_array( $ids ) ) { $ids = implode( ',', $ids ); } $ids = explode( ',', (string) $ids ); $ids = array_map( 'intval', $ids ); $placeholders = implode( ', ', array_fill( 0, count( $ids ), '%d' ) ); $table = self::get_table_name(); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare $events_data = $wpdb->get_results( $wpdb->prepare( "SELECT id, content, initiator, event_type, created_at FROM {$table} WHERE id IN ( {$placeholders} )", $ids ) ); // phpcs:enable if ( empty( $events_data ) ) { return []; } return array_map( function ( $event_item ) { $event = new Event( $event_item ); return $event->get_short_details(); }, $events_data ); } /** * Returns the number of error debug events in a given time span. * * By default it returns the number of error debug events in the last 30 days. * * @since 3.9.0 * * @param string $span_of_time The time span to count the events for. Default '-30 days'. * * @return int|WP_Error The number of error debug events or WP_Error on failure. */ public static function get_error_debug_events_count( $span_of_time = '-30 days' ) { $timestamp = strtotime( $span_of_time ); if ( ! $timestamp || $timestamp > time() ) { return new WP_Error( 'wp_mail_smtp_admin_debug_events_get_error_debug_events_count_invalid_time', 'Invalid time span.' ); } if ( ! self::is_valid_db() ) { return 0; } $transient_key = self::ERROR_DEBUG_EVENTS_TRANSIENT . '_' . sanitize_title_with_dashes( $span_of_time ); $cached_error_events_count = get_transient( $transient_key ); if ( $cached_error_events_count !== false ) { return (int) $cached_error_events_count; } global $wpdb; // phpcs:disable WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder $sql = $wpdb->prepare( 'SELECT COUNT(*) FROM `%1$s` WHERE event_type = %2$d AND created_at >= "%3$s"', self::get_table_name(), Event::TYPE_ERROR, gmdate( WP::datetime_mysql_format(), $timestamp ) ); // phpcs:enable WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $error_events_count = (int) $wpdb->get_var( $sql ); set_transient( $transient_key, $error_events_count, HOUR_IN_SECONDS ); return $error_events_count; } /** * Register the screen options for the debug events page. * * @since 3.0.0 */ public function screen_options() { $screen = get_current_screen(); if ( ! is_object( $screen ) || strpos( $screen->id, 'wp-mail-smtp_page_wp-mail-smtp-tools' ) === false || // phpcs:ignore WordPress.Security.NonceVerification.Recommended ! isset( $_GET['tab'] ) || $_GET['tab'] !== 'debug-events' ) { return; } add_screen_option( 'per_page', [ 'label' => esc_html__( 'Number of events per page:', 'wp-mail-smtp' ), 'option' => 'wp_mail_smtp_debug_events_per_page', 'default' => EventsCollection::PER_PAGE, ] ); } /** * Set the screen options for the debug events page. * * @since 3.0.0 * * @param bool $keep Whether to save or skip saving the screen option value. * @param string $option The option name. * @param int $value The number of items to use. * * @return bool|int */ public function set_screen_options( $keep, $option, $value ) { if ( 'wp_mail_smtp_debug_events_per_page' === $option ) { return (int) $value; } return $keep; } /** * Whether the email debug for debug events is enabled or not. * * @since 3.0.0 * * @return bool */ public static function is_debug_enabled() { return (bool) Options::init()->get( 'debug_events', 'email_debug' ); } /** * Get the debug events page URL. * * @since 3.0.0 * * @return string */ public static function get_page_url() { return add_query_arg( [ 'tab' => 'debug-events', ], wp_mail_smtp()->get_admin()->get_admin_page_url( Area::SLUG . '-tools' ) ); } /** * Get the DB table name. * * @since 3.0.0 * * @return string Table name, prefixed. */ public static function get_table_name() { global $wpdb; return $wpdb->prefix . 'wpmailsmtp_debug_events'; } /** * Whether the DB table exists. * * @since 3.0.0 * * @return bool */ public static function is_valid_db() { global $wpdb; static $is_valid = null; // Return cached value only if table already exists. if ( $is_valid === true ) { return true; } $table = self::get_table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching $is_valid = (bool) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s;', $table ) ); return $is_valid; } } Migration.php 0000644 00000003055 15253207572 0007221 0 ustar 00 <?php namespace WPMailSMTP\Admin\DebugEvents; use WPMailSMTP\MigrationAbstract; /** * Debug Events Migration Class * * @since 3.0.0 */ class Migration extends MigrationAbstract { /** * Version of the debug events database table. * * @since 3.0.0 */ const DB_VERSION = 1; /** * Option key where we save the current debug events DB version. * * @since 3.0.0 */ const OPTION_NAME = 'wp_mail_smtp_debug_events_db_version'; /** * Option key where we save any errors while creating the debug events DB table. * * @since 3.0.0 */ const ERROR_OPTION_NAME = 'wp_mail_smtp_debug_events_db_error'; /** * Create the debug events DB table structure. * * @since 3.0.0 */ protected function migrate_to_1() { global $wpdb; $table = DebugEvents::get_table_name(); $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS `$table` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `content` TEXT DEFAULT NULL, `initiator` TEXT DEFAULT NULL, `event_type` TINYINT UNSIGNED NOT NULL DEFAULT '0', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINE='InnoDB' {$charset_collate};"; $result = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared if ( ! empty( $wpdb->last_error ) ) { update_option( self::ERROR_OPTION_NAME, $wpdb->last_error, false ); } // Save the current version to DB. if ( $result !== false ) { $this->update_db_ver( 1 ); } } } GetDebugEventsAbility.php 0000644 00000007565 15253212417 0011465 0 ustar 00 <?php namespace WPMailSMTP\Abilities\DebugEvents; use WPMailSMTP\Abilities\AbstractAbility; use WPMailSMTP\Admin\DebugEvents\Event; use WPMailSMTP\Admin\DebugEvents\EventsCollection; /** * Ability: list recorded WP Mail SMTP debug events. * * Edition-neutral: registered on every install (Lite and Pro). Gated on the * plugin's manage-options capability, the same gate that controls the Debug * Events screen. * * @since 4.9.0 */ class GetDebugEventsAbility extends AbstractAbility { /** * Ability slug, without the namespace prefix. * * @since 4.9.0 * * @return string */ public function get_name() { return 'get-debug-events'; } /** * Human-readable label. * * @since 4.9.0 * * @return string */ public function get_label() { return esc_html__( 'Get Debug Events', 'wp-mail-smtp' ); } /** * Human-readable description. * * @since 4.9.0 * * @return string */ public function get_description() { return esc_html__( 'List recorded WP Mail SMTP debug events (errors and debug entries).', 'wp-mail-smtp' ); } /** * Input schema. * * @since 4.9.0 * * @return array */ public function get_input_schema() { return [ 'type' => 'object', 'properties' => [ 'limit' => [ 'description' => esc_html__( 'Maximum number of events to return.', 'wp-mail-smtp' ), 'type' => 'integer', 'minimum' => 1, 'maximum' => 100, 'default' => 20, ], 'offset' => [ 'description' => esc_html__( 'Number of events to skip.', 'wp-mail-smtp' ), 'type' => 'integer', 'minimum' => 0, 'default' => 0, ], 'severity' => [ 'description' => esc_html__( 'Filter events by severity.', 'wp-mail-smtp' ), 'type' => 'string', 'enum' => [ 'error', 'debug' ], ], ], ]; } /** * Output schema. * * @since 4.9.0 * * @return array */ public function get_output_schema() { return [ 'type' => 'object', 'properties' => [ 'events' => [ 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'id' => [ 'type' => 'integer' ], 'created_date' => [ 'type' => 'string' ], 'severity' => [ 'type' => 'string' ], 'content' => [ 'type' => 'string' ], ], ], ], 'total' => [ 'type' => 'integer' ], 'limit' => [ 'type' => 'integer' ], 'offset' => [ 'type' => 'integer' ], ], ]; } /** * Execute: list debug events. * * @since 4.9.0 * * @param mixed $input Input data. * * @return array */ public function execute( $input = null ) { $args = $this->normalize_input( $input ); $pagination = $this->get_pagination( $args ); $limit = $pagination['limit']; $offset = $pagination['offset']; $params = [ 'per_page' => $limit, 'offset' => $offset, ]; if ( isset( $args['severity'] ) ) { $severity = sanitize_text_field( $args['severity'] ); if ( $severity === 'error' ) { $params['type'] = Event::TYPE_ERROR; } elseif ( $severity === 'debug' ) { $params['type'] = Event::TYPE_DEBUG; } } $collection = new EventsCollection( $params ); $total = $collection->get_count(); $events = []; foreach ( $collection->get() as $event ) { $events[] = $this->format_event( $event ); } return [ 'events' => $events, 'total' => $total, 'limit' => $limit, 'offset' => $offset, ]; } /** * Format a single debug event for output. * * @since 4.9.0 * * @param Event $event Debug event model. * * @return array */ private function format_event( Event $event ) { return [ 'id' => $event->get_id(), 'created_date' => $this->to_iso8601( $event->get_created_at() ), 'severity' => $event->get_type() === Event::TYPE_ERROR ? 'error' : 'debug', 'content' => $event->get_content(), ]; } }
dvadf
dvadf
| ver. 1.4 |
Github
|
.
| PHP 7.3.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings