Plugin Directory

Changeset 3451291


Ignore:
Timestamp:
02/01/2026 09:15:18 AM (5 weeks ago)
Author:
awesomefootnotes
Message:

Adding the first version of my plugin

Location:
0-day-analytics
Files:
38 edited
1 copied

Legend:

Unmodified
Added
Removed
  • 0-day-analytics/tags/4.7.0/advanced-analytics.php

    r3448917 r3451291  
    1111 * Plugin Name:     0 Day Analytics
    1212 * Description:     Take full control of error log, crons, transients, plugins, requests, mails and DB tables.
    13  * Version:         4.6.0
     13 * Version:         4.7.0
    1414 * Author:          Stoil Dobrev
    1515 * Author URI:      https://github.com/sdobreff/
     
    3939// Constants.
    4040if ( ! defined( 'ADVAN_VERSION' ) ) {
    41     define( 'ADVAN_VERSION', '4.6.0' );
     41    define( 'ADVAN_VERSION', '4.7.0' );
    4242    define( 'ADVAN_TEXTDOMAIN', '0-day-analytics' );
    4343    define( 'ADVAN_NAME', '0 Day Analytics' );
  • 0-day-analytics/tags/4.7.0/classes/migration/class-migration.php

    r3448917 r3451291  
    1414
    1515use ADVAN\Helpers\Settings;
     16use ADVAN\Entities\WP_Mail_Entity;
     17use ADVAN\Entities\WP_Fatals_Entity;
     18use ADVAN\Entities\Requests_Log_Entity;
    1619use ADVAN\Entities_Global\Common_Table;
    17 use ADVAN\Entities\WP_Mail_Entity;
    18 use ADVAN\Entities\Requests_Log_Entity;
    1920use ADVAN\Migration\Abstract_Migration;
    2021
     
    267268            }
    268269        }
     270
     271        /**
     272         * Migrates the plugin up-to version 4.7.0 (adds performance indexes to fatals table).
     273         *
     274         * @return void
     275         *
     276         * @since 4.7.0
     277         */
     278        public static function migrate_up_to_470() {
     279            if ( \class_exists( '\\ADVAN\\Entities\\WP_Fatals_Entity' ) ) {
     280                if ( Common_Table::check_table_exists( WP_Fatals_Entity::get_table_name() ) ) {
     281                    WP_Fatals_Entity::alter_table_470();
     282                }
     283            }
     284        }
    269285    }
    270286}
  • 0-day-analytics/tags/4.7.0/classes/vendor/controllers/class-hooks-capture.php

    r3448917 r3451291  
    192192
    193193            // In WP-CLI context, ensure hooks are attached properly.
    194             if ( defined( 'WP_CLI' ) && WP_CLI ) {
     194            if ( defined( 'WP_CLI' ) && \WP_CLI ) {
    195195                self::attach_hooks_cli();
    196196            } else {
  • 0-day-analytics/tags/4.7.0/classes/vendor/controllers/class-reverse-line-reader.php

    r3393178 r3451291  
    2828
    2929        /**
     30         * Memory limit in MB before triggering cleanup or termination
     31         */
     32        const MEMORY_LIMIT_MB = 32;
     33
     34        /**
     35         * Time limit in seconds for processing
     36         */
     37        const TIME_LIMIT_SECONDS = 25;
     38
     39        /**
     40         * Maximum number of collected items to prevent memory exhaustion
     41         */
     42        const MAX_COLLECTED_ITEMS = 1000;
     43
     44        /**
    3045         * Keeps track of of the current position in the file.
    3146         *
     
    8095         */
    8196        private static $memory_handle = null;
     97
     98        /**
     99         * Stores the overflow file handle for spilling data to disk when memory is critical.
     100         *
     101         * @var resource|null
     102         *
     103         * @since 1.9.3
     104         */
     105        private static $overflow_handle = null;
    82106
    83107        /**
     
    146170                }
    147171
     172                // Adaptive buffer sizing based on available memory.
     173                $available_memory = self::get_available_memory_mb();
     174                if ( $available_memory < 50 ) { // Low memory system.
     175                    self::$buffer_size = min( self::$buffer_size, 4096 ); // Use smaller buffer.
     176                } elseif ( $available_memory < 100 ) { // Medium memory system.
     177                    self::$buffer_size = min( self::$buffer_size, 8192 ); // Use medium buffer.
     178                }
     179                // Otherwise use default BUFFER_SIZE.
     180
    148181                self::$file_size = - (int) $size;
    149182            }
     183
     184            // Initialize memory and time monitoring.
     185            $start_time      = time();
     186            $start_memory    = memory_get_usage( true );
     187            $processed_lines = 0;
    150188
    151189            $line = self::readline();
     
    165203            }
    166204            $result = $callback( $line, self::$pos );
     205
     206            // Memory and time safety checks.
     207            ++$processed_lines;
     208            $current_memory_mb = ( memory_get_usage( true ) - $start_memory ) / 1024 / 1024;
     209            $elapsed_time      = time() - $start_time;
     210
     211            // Terminate if memory limit exceeded.
     212            if ( $current_memory_mb > self::MEMORY_LIMIT_MB ) {
     213                if ( function_exists( 'error_log' ) ) {
     214                    error_log( 'Reverse_Line_Reader: Memory limit exceeded (' . round( $current_memory_mb, 2 ) . 'MB), terminating processing' );
     215                }
     216                self::reset_class_globals();
     217                $max_lines = 0;
     218                return false;
     219            }
     220
     221            // Create overflow file if memory is getting critical (80% of limit).
     222            if ( $current_memory_mb > ( self::MEMORY_LIMIT_MB * 0.8 ) && null === self::$overflow_handle ) {
     223                self::$overflow_handle = self::create_overflow_temp_file();
     224                if ( self::$overflow_handle && function_exists( 'error_log' ) ) {
     225                    error_log( 'Reverse_Line_Reader: Created overflow file due to high memory usage (' . round( $current_memory_mb, 2 ) . 'MB)' );
     226                }
     227            }
     228
     229            // Terminate if time limit exceeded.
     230            if ( $elapsed_time > self::TIME_LIMIT_SECONDS ) {
     231                if ( function_exists( 'error_log' ) ) {
     232                    error_log( 'Reverse_Line_Reader: Time limit exceeded (' . $elapsed_time . 's), terminating processing' );
     233                }
     234                self::reset_class_globals();
     235                $max_lines = 0;
     236                return false;
     237            }
    167238
    168239            if ( true === $result['close'] ) {
     
    283354         */
    284355        public static function write_memory_file( string $line ) {
     356            // If overflow file is active, write to it instead of memory.
     357            if ( null !== self::$overflow_handle ) {
     358                fwrite( self::$overflow_handle, $line ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
     359                return;
     360            }
     361
    285362            if ( null === self::$memory_handle ) {
    286363                self::$memory_handle = fopen( 'php://memory', 'w+' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
     
    301378                rewind( self::$temp_handle ); // resets the position of pointer.
    302379
     380                // Content is pre-escaped with esc_html() before being written to temp file
    303381                echo fread( self::$temp_handle, fstat( self::$temp_handle )['size'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
    304382
     
    315393         */
    316394        public static function read_memory_file() {
    317             if ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
     395            // If overflow file exists, read from it instead.
     396            if ( \is_resource( self::$overflow_handle ) && ( 'handle' === get_resource_type( self::$overflow_handle ) || 'stream' === get_resource_type( self::$overflow_handle ) ) ) {
     397                rewind( self::$overflow_handle ); // resets the position of pointer.
     398
     399                // Content is pre-escaped with esc_html() before being written to overflow file.
     400                echo fread( self::$overflow_handle, fstat( self::$overflow_handle )['size'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
     401
     402                fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     403                self::$overflow_handle = null;
     404            } elseif ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
    318405                rewind( self::$memory_handle ); // resets the position of pointer.
    319406
     407                // Content is pre-escaped with esc_html() before being written to memory file.
    320408                echo fread( self::$memory_handle, fstat( self::$memory_handle )['size'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
    321409
     
    334422         */
    335423        public static function flush_memory_file_to_temp() {
    336             if ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
    337 
     424            // Handle overflow file first if it exists.
     425            if ( \is_resource( self::$overflow_handle ) && ( 'handle' === get_resource_type( self::$overflow_handle ) || 'stream' === get_resource_type( self::$overflow_handle ) ) ) {
    338426                $line = '';
    339                 for ( $x_pos = 0; fseek( self::$memory_handle, $x_pos, SEEK_END ) !== -1; $x_pos-- ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
    340                     $char = fgetc( self::$memory_handle );
     427                for ( $x_pos = 0; fseek( self::$overflow_handle, $x_pos, SEEK_END ) !== -1; $x_pos-- ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
     428                    $char = fgetc( self::$overflow_handle );
    341429
    342430                    if ( PHP_EOL === $char ) {
     
    351439                    self::write_temp_file( $line . PHP_EOL );
    352440                }
     441                fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     442                self::$overflow_handle = null;
     443            } elseif ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
     444                $line = '';
     445                for ( $x_pos = 0; fseek( self::$memory_handle, $x_pos, SEEK_END ) !== -1; $x_pos-- ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
     446                    $char = fgetc( self::$memory_handle );
     447
     448                    if ( PHP_EOL === $char ) {
     449                        self::write_temp_file( $line . PHP_EOL );
     450                        $line = '';
     451                        continue;
     452                    } else {
     453                        $line = $char . $line;
     454                    }
     455                }
     456                if ( ! empty( $line ) ) {
     457                    self::write_temp_file( $line . PHP_EOL );
     458                }
    353459                fclose( self::$memory_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
    354 
    355460                self::$memory_handle = null;
    356461            }
     
    375480                self::$memory_handle = null;
    376481            }
     482            if ( \is_resource( self::$overflow_handle ) && ( 'handle' === get_resource_type( self::$overflow_handle ) || 'stream' === get_resource_type( self::$overflow_handle ) ) ) {
     483                fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     484
     485                self::$overflow_handle = null;
     486            }
    377487        }
    378488
     
    387497            if ( \is_resource( self::$error_log_handle ) && ( 'handle' === get_resource_type( self::$error_log_handle ) || 'stream' === get_resource_type( self::$error_log_handle ) ) ) {
    388498                \fclose( self::$error_log_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     499            }
     500
     501            if ( \is_resource( self::$overflow_handle ) ) {
     502                \fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
    389503            }
    390504
     
    393507            self::$pos              = null;
    394508            self::$error_log_handle = null;
     509            self::$overflow_handle  = null;
     510        }
     511
     512        /**
     513         * Get available memory in MB
     514         *
     515         * @return int Available memory in MB
     516         *
     517         * @since latest
     518         */
     519        public static function get_available_memory_mb() {
     520            $memory_limit = ini_get( 'memory_limit' );
     521            if ( '-1' === $memory_limit ) {
     522                return 256; // Assume 256MB if no limit.
     523            }
     524
     525            $memory_limit_bytes = wp_convert_hr_to_bytes( $memory_limit );
     526            $current_usage      = memory_get_usage( true );
     527            $available          = max( 0, $memory_limit_bytes - $current_usage );
     528
     529            return (int) ( $available / 1024 / 1024 );
     530        }
     531
     532        /**
     533         * Check if available memory is below critical threshold
     534         *
     535         * @return bool True if memory is critical, false otherwise
     536         *
     537         * @since latest
     538         */
     539        public static function is_memory_critical() {
     540            $available_mb = self::get_available_memory_mb();
     541            return $available_mb < 10; // Less than 10MB available.
    395542        }
    396543
     
    451598            return self::$temp_handle;
    452599        }
     600
     601        /**
     602         * Create a temporary file for overflow data when memory is critical
     603         *
     604         * @return resource|false File handle or false on failure
     605         *
     606         * @since latest
     607         */
     608        public static function create_overflow_temp_file() {
     609            $temp_dir  = get_temp_dir();
     610            $temp_file = tempnam( $temp_dir, 'advana_overflow_' );
     611
     612            if ( $temp_file && is_writable( $temp_file ) ) {
     613                return fopen( $temp_file, 'w+' );
     614            }
     615
     616            return false;
     617        }
     618
     619        /**
     620         * Clean up temporary overflow files
     621         *
     622         * @return void
     623         *
     624         * @since latest
     625         */
     626        public static function cleanup_overflow_files() {
     627            $temp_dir = get_temp_dir();
     628            $pattern  = $temp_dir . '/advana_overflow_*';
     629
     630            foreach ( glob( $pattern ) as $file ) {
     631                if ( is_file( $file ) && filemtime( $file ) < time() - 3600 ) { // Older than 1 hour.
     632                    unlink( $file );
     633                }
     634            }
     635        }
    453636    }
    454637}
  • 0-day-analytics/tags/4.7.0/classes/vendor/controllers/classes-snippets-controller.php

    r3413502 r3451291  
    141141            }
    142142        }
     143
    143144        /**
    144145         * Fetch and cache runtime snippets list.
     
    153154            }
    154155
    155             $wpdb  = Snippet_Entity::get_connection();
    156             $table = Snippet_Entity::get_table_name( $wpdb );
    157 
    158             $query = $wpdb->prepare(
    159                 'SELECT * FROM ' . $table . ' WHERE blog_id = %d AND status = %d AND type = %s',
    160                 \get_current_blog_id(),
    161                 Snippet_Entity::STATUS_ENABLED,
    162                 'php'
    163             );
    164 
    165             $wpdb->suppress_errors( true );
    166             $results = $wpdb->get_results( $query, ARRAY_A );
    167             if ( '' !== $wpdb->last_error ) {
    168                 if ( 1146 === Snippet_Entity::get_last_sql_error( $wpdb ) ) {
    169                     if ( Snippet_Entity::create_table( $wpdb ) ) {
    170                         $results = array();
    171                     }
    172                 }
    173             }
    174             $wpdb->suppress_errors( false );
    175 
    176             self::$runtime_snippets = is_array( $results ) ? $results : array();
     156            self::$runtime_snippets = Snippet_Entity::get_runtime_snippets();
    177157
    178158            return self::$runtime_snippets;
  • 0-day-analytics/tags/4.7.0/classes/vendor/entities/class-hooks-management-entity.php

    r3442473 r3451291  
    24742474            }
    24752475
    2476             // Add the ID to the data for the update operation.
    2477             $data['id'] = $id;
    2478 
    2479             $result = self::insert( $data );
     2476            // Load current record.
     2477            $current = self::load( 'id = %d', $id );
     2478            if ( ! $current ) {
     2479                return false;
     2480            }
     2481
     2482            // Merge new data with existing data.
     2483            $current = array_merge( $current, $data );
     2484
     2485            $result = self::insert( $current );
    24802486
    24812487            // Clear cache.
    24822488            \wp_cache_delete( 'advan_enabled_hooks', 'advan' );
     2489            self::clear_hook_labels_cache();
    24832490
    24842491            return $result > 0;
     2492        }
     2493
     2494        /**
     2495         * Set enabled status for a hook.
     2496         *
     2497         * @param int  $id      Hook ID.
     2498         * @param bool $enabled Enabled status.
     2499         *
     2500         * @return bool
     2501         *
     2502         * @since 4.7.0
     2503         */
     2504        public static function set_enabled( int $id, bool $enabled ): bool {
     2505            return self::update(
     2506                $id,
     2507                array(
     2508                    'enabled'       => $enabled ? 1 : 0,
     2509                    'date_modified' => microtime( true ),
     2510                )
     2511            );
     2512        }
     2513
     2514        /**
     2515         * Set group ID for a hook.
     2516         *
     2517         * @param int $id       Hook ID.
     2518         * @param int $group_id Group ID.
     2519         *
     2520         * @return bool
     2521         *
     2522         * @since 4.7.0
     2523         */
     2524        public static function set_group_id( int $id, int $group_id ): bool {
     2525            return self::update(
     2526                $id,
     2527                array(
     2528                    'group_id'      => $group_id,
     2529                    'date_modified' => microtime( true ),
     2530                )
     2531            );
     2532        }
     2533
     2534        /**
     2535         * Get hook parameters by hook name.
     2536         *
     2537         * @param string $hook_name Hook name.
     2538         *
     2539         * @return string
     2540         *
     2541         * @since 4.7.0
     2542         */
     2543        public static function get_hook_parameters( string $hook_name ): string {
     2544            $hook = self::load( 'hook_name = %s', array( $hook_name ) );
     2545            return $hook && isset( $hook['hook_parameters'] ) ? $hook['hook_parameters'] : '';
    24852546        }
    24862547    }
  • 0-day-analytics/tags/4.7.0/classes/vendor/entities/class-snippet-entity.php

    r3413502 r3451291  
    640640            );
    641641        }
     642
     643        /**
     644         * Get runtime snippets (enabled PHP snippets for current blog).
     645         *
     646         * @return array
     647         *
     648         * @since 4.3.0
     649         */
     650        public static function get_runtime_snippets(): array {
     651            $wpdb    = self::get_connection();
     652            $table   = self::get_table_name( $wpdb );
     653            $blog_id = \get_current_blog_id();
     654
     655            $query = $wpdb->prepare(
     656                'SELECT * FROM ' . $table . ' WHERE blog_id = %d AND status = %d AND type = %s',
     657                $blog_id,
     658                self::STATUS_ENABLED,
     659                'php'
     660            );
     661
     662            $wpdb->suppress_errors( true );
     663            $results = $wpdb->get_results( $query, ARRAY_A );
     664            if ( '' !== $wpdb->last_error ) {
     665                if ( 1146 === self::get_last_sql_error( $wpdb ) ) {
     666                    if ( self::create_table( $wpdb ) ) {
     667                        $results = array();
     668                    }
     669                }
     670            }
     671            $wpdb->suppress_errors( false );
     672
     673            return is_array( $results ) ? $results : array();
     674        }
     675
     676        /**
     677         * Get snippets with filters for AJAX listing.
     678         *
     679         * @param array $filters Array of filters (search, status, type, etc.).
     680         * @param int   $offset  Pagination offset.
     681         * @param int   $limit   Pagination limit.
     682         *
     683         * @return array
     684         *
     685         * @since 4.3.0
     686         */
     687        public static function get_snippets_with_filters( array $filters = array(), int $offset = 0, int $limit = 50 ): array {
     688            $wpdb    = self::get_connection();
     689            $table   = self::get_table_name( $wpdb );
     690            $blog_id = \get_current_blog_id();
     691
     692            $where    = array( 'blog_id = %d' );
     693            $bindings = array( $blog_id );
     694
     695            // Search filter
     696            if ( ! empty( $filters['search'] ) ) {
     697                $search = '%' . $wpdb->esc_like( $filters['search'] ) . '%';
     698                $where[] = '(name LIKE %s OR tags LIKE %s)';
     699                $bindings[] = $search;
     700                $bindings[] = $search;
     701            }
     702
     703            // Status filter
     704            if ( isset( $filters['status'] ) ) {
     705                $status_map = array(
     706                    'enabled'  => self::STATUS_ENABLED,
     707                    'disabled' => self::STATUS_DISABLED,
     708                    'trash'    => self::STATUS_TRASHED,
     709                );
     710
     711                if ( isset( $status_map[ $filters['status'] ] ) ) {
     712                    $where[] = 'status = %d';
     713                    $bindings[] = $status_map[ $filters['status'] ];
     714                } else {
     715                    $where[] = 'status >= %d';
     716                    $bindings[] = self::STATUS_DISABLED;
     717                }
     718            }
     719
     720            // Type filter
     721            if ( ! empty( $filters['type'] ) && in_array( $filters['type'], array_keys( self::get_supported_types() ), true ) ) {
     722                $where[] = 'type = %s';
     723                $bindings[] = $filters['type'];
     724            }
     725
     726            $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';
     727            $orderby   = 'updated_at';
     728            $order     = 'DESC';
     729
     730            $query = 'SELECT * FROM ' . $table . ' ' . $where_sql . ' ORDER BY ' . $orderby . ' ' . $order . ' LIMIT %d OFFSET %d';
     731            $bindings[] = $limit;
     732            $bindings[] = $offset;
     733
     734            $wpdb->suppress_errors( true );
     735            $results = $wpdb->get_results( $wpdb->prepare( $query, $bindings ), ARRAY_A );
     736            if ( '' !== $wpdb->last_error ) {
     737                if ( 1146 === self::get_last_sql_error( $wpdb ) ) {
     738                    if ( self::create_table( $wpdb ) ) {
     739                        $results = array();
     740                    }
     741                }
     742            }
     743            $wpdb->suppress_errors( false );
     744
     745            return is_array( $results ) ? $results : array();
     746        }
     747
     748        /**
     749         * Get total count of snippets with filters.
     750         *
     751         * @param array $filters Array of filters (search, status, type, etc.).
     752         *
     753         * @return int
     754         *
     755         * @since 4.3.0
     756         */
     757        public static function get_snippets_count_with_filters( array $filters = array() ): int {
     758            $wpdb    = self::get_connection();
     759            $table   = self::get_table_name( $wpdb );
     760            $blog_id = \get_current_blog_id();
     761
     762            $where    = array( 'blog_id = %d' );
     763            $bindings = array( $blog_id );
     764
     765            // Search filter.
     766            if ( ! empty( $filters['search'] ) ) {
     767                $search = '%' . $wpdb->esc_like( $filters['search'] ) . '%';
     768                $where[] = '(name LIKE %s OR tags LIKE %s)';
     769                $bindings[] = $search;
     770                $bindings[] = $search;
     771            }
     772
     773            // Status filter.
     774            if ( isset( $filters['status'] ) ) {
     775                $status_map = array(
     776                    'enabled'  => self::STATUS_ENABLED,
     777                    'disabled' => self::STATUS_DISABLED,
     778                    'trash'    => self::STATUS_TRASHED,
     779                );
     780
     781                if ( isset( $status_map[ $filters['status'] ] ) ) {
     782                    $where[] = 'status = %d';
     783                    $bindings[] = $status_map[ $filters['status'] ];
     784                } else {
     785                    $where[] = 'status >= %d';
     786                    $bindings[] = self::STATUS_DISABLED;
     787                }
     788            }
     789
     790            // Type filter.
     791            if ( ! empty( $filters['type'] ) && in_array( $filters['type'], array_keys( self::get_supported_types() ), true ) ) {
     792                $where[] = 'type = %s';
     793                $bindings[] = $filters['type'];
     794            }
     795
     796            $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';
     797            $query = 'SELECT COUNT(*) FROM ' . $table . ' ' . $where_sql;
     798
     799            $wpdb->suppress_errors( true );
     800            $count = (int) $wpdb->get_var( $wpdb->prepare( $query, $bindings ) );
     801            if ( '' !== $wpdb->last_error ) {
     802                if ( 1146 === self::get_last_sql_error( $wpdb ) ) {
     803                    if ( self::create_table( $wpdb ) ) {
     804                        $count = 0;
     805                    }
     806                }
     807            }
     808            $wpdb->suppress_errors( false );
     809
     810            return $count;
     811        }
    642812    }
    643813}
  • 0-day-analytics/tags/4.7.0/classes/vendor/entities/class-wp-fatals-entity.php

    r3393178 r3451291  
    651651
    652652        /**
     653         * Alters the table for version 4.7.0 - adds performance indexes
     654         *
     655         * @return bool
     656         *
     657         * @since 4.7.0
     658         */
     659        public static function alter_table_470(): bool {
     660            $table_name = self::get_table_name();
     661            $indexes    = array(
     662                'severity'    => 'ADD KEY severity (severity)',
     663                'source_slug' => 'ADD KEY source_slug (source_slug)',
     664                'source_type' => 'ADD KEY source_type (source_type)',
     665            );
     666
     667            foreach ( $indexes as $index_sql ) {
     668                $sql = "ALTER TABLE `$table_name` $index_sql";
     669                // Use try-catch or check if index exists by attempting the alter
     670                // Since MySQL ignores duplicate key errors, we can safely run this.
     671                self::get_connection()->query( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- DDL operation, no caching needed.
     672            }
     673
     674            return true;
     675        }
     676
     677        /**
     678         * Prunes old fatal error records based on retention settings
     679         *
     680         * @param int $retention_days - Number of days to keep records (default 90)
     681         *
     682         * @return int Number of records deleted
     683         *
     684         * @since 4.7.0
     685         */
     686        public static function prune_old_records( int $retention_days = 90 ): int {
     687            global $wpdb;
     688            $cutoff_time = time() - ( $retention_days * 24 * 60 * 60 );
     689            $table_name  = self::get_table_name();
     690
     691            $sql = $wpdb->prepare(
     692                "DELETE FROM `$table_name` WHERE datetime < %d",
     693                $cutoff_time
     694            );
     695
     696            $result = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk delete operation.
     697
     698            return (int) $result;
     699        }
     700
     701        /**
    653702         * Generates drop down with all the subsites that have mail logs.
    654703         *
  • 0-day-analytics/tags/4.7.0/classes/vendor/helpers/class-ajax-helper.php

    r3442115 r3451291  
    153153                \add_action( 'wp_ajax_aadvana_export_large_csv', array( __CLASS__, 'export_large_csv' ) );
    154154                \add_action( 'wp_ajax_aadvana_export_large_csv_cleanup', array( __CLASS__, 'export_large_csv_cleanup' ) );
     155                \add_action( 'wp_ajax_aadvana_get_table_info', array( __CLASS__, 'get_table_info' ) );
    155156
    156157                if ( Settings::get_option( 'server_info_module_enabled' ) ) {
     
    975976                    $snippet_type   = isset( $_POST['snippet_type'] ) ? \sanitize_text_field( wp_unslash( $_POST['snippet_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
    976977
    977                     $extra_file_name = '_snippets_';
    978 
    979                     $wpdb  = \ADVAN\Entities\Snippet_Entity::get_connection();
    980                     $table = \ADVAN\Entities\Snippet_Entity::get_table_name( $wpdb );
    981 
    982                     $where    = array( 'blog_id = %d' );
    983                     $bindings = array( get_current_blog_id() );
     978                    $filters = array();
    984979
    985980                    if ( '' !== $search ) {
    986                         $like       = '%' . $wpdb->esc_like( $search ) . '%';
    987                         $where[]    = '( name LIKE %s OR tags LIKE %s )';
    988                         $bindings[] = $like;
    989                         $bindings[] = $like;
     981                        $filters['search'] = $search;
    990982                    }
    991983
    992                     $status_map = array(
    993                         'enabled'  => \ADVAN\Entities\Snippet_Entity::STATUS_ENABLED,
    994                         'disabled' => \ADVAN\Entities\Snippet_Entity::STATUS_DISABLED,
    995                         'trash'    => \ADVAN\Entities\Snippet_Entity::STATUS_TRASHED,
    996                     );
    997 
    998                     if ( isset( $status_map[ $snippet_status ] ) ) {
    999                         $where[]    = 'status = %d';
    1000                         $bindings[] = $status_map[ $snippet_status ];
    1001                     } else {
    1002                         $where[]    = 'status >= %d';
    1003                         $bindings[] = \ADVAN\Entities\Snippet_Entity::STATUS_DISABLED;
     984                    if ( '' !== $snippet_status ) {
     985                        $filters['status'] = $snippet_status;
    1004986                    }
    1005987
    1006                     $types = array_keys( \ADVAN\Entities\Snippet_Entity::get_supported_types() );
    1007                     if ( '' !== $snippet_type && in_array( $snippet_type, $types, true ) ) {
    1008                         $where[]    = 'type = %s';
    1009                         $bindings[] = $snippet_type;
     988                    if ( '' !== $snippet_type ) {
     989                        $filters['type'] = $snippet_type;
    1010990                    }
    1011991
    1012                     $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';
    1013 
    1014                     $count_sql = 'SELECT COUNT(*) FROM ' . $table . ' ' . $where_sql;
    1015                     $total     = (int) $wpdb->get_var( $wpdb->prepare( $count_sql, $bindings ) );
    1016 
    1017                     $orderby = 'updated_at';
    1018                     $order   = 'DESC';
    1019 
    1020                     $list_sql   = 'SELECT * FROM ' . $table . ' ' . $where_sql . ' ORDER BY ' . $orderby . ' ' . $order . ' LIMIT %d OFFSET %d';
    1021                     $list_items = $wpdb->get_results(
    1022                         $wpdb->prepare(
    1023                             $list_sql,
    1024                             array_merge( $bindings, array( (int) $batch_size, (int) $offset ) )
    1025                         ),
    1026                         ARRAY_A
    1027                     );
    1028 
    1029                     $rows = $list_items ?: array();
     992                    $total = \ADVAN\Entities\Snippet_Entity::get_snippets_count_with_filters( $filters );
     993                    $rows  = \ADVAN\Entities\Snippet_Entity::get_snippets_with_filters( $filters, $offset, $batch_size );
    1030994                }
    1031995            } else {
     
    11761140         */
    11771141        public static function save_hook_group() {
    1178             if ( ! \current_user_can( 'manage_options' ) ) {
    1179                 \wp_send_json_error( 'Unauthorized' );
    1180             }
    1181 
    1182             \check_ajax_referer( 'advan_hook_groups', 'nonce' );
     1142            WP_Helper::verify_admin_nonce( 'advan_hook_groups', 'nonce' );
    11831143
    11841144            if ( ! class_exists( 'ADVAN\Entities\Hook_Groups_Entity' ) ) {
     
    12181178         */
    12191179        public static function delete_hook_group() {
    1220             if ( ! \current_user_can( 'manage_options' ) ) {
    1221                 \wp_send_json_error( 'Unauthorized' );
    1222             }
    1223 
    1224             \check_ajax_referer( 'advan_hook_groups', 'nonce' );
     1180            WP_Helper::verify_admin_nonce( 'advan_hook_groups', 'nonce' );
    12251181
    12261182            if ( ! class_exists( 'ADVAN\Entities\Hook_Groups_Entity' ) ) {
     
    12421198            }
    12431199        }
     1200
     1201        /**
     1202         * Get detailed table information including structure, indexes, foreign keys, and health.
     1203         *
     1204         * @return void
     1205         *
     1206         * @since 4.7.0
     1207         */
     1208        public static function get_table_info() {
     1209            WP_Helper::verify_admin_nonce( 'get_table_info', 'security' );
     1210
     1211            $table_name = isset( $_POST['table_name'] ) ? \sanitize_text_field( \wp_unslash( $_POST['table_name'] ) ) : '';
     1212
     1213            if ( empty( $table_name ) ) {
     1214                \wp_send_json_error( 'Invalid table name' );
     1215            }
     1216
     1217            $table_info = Common_Table::get_table_info( $table_name );
     1218
     1219            if ( \is_wp_error( $table_info ) ) {
     1220                \wp_send_json_error( $table_info->get_error_message() );
     1221            }
     1222
     1223            \wp_send_json_success( $table_info );
     1224        }
    12441225    }
    12451226}
  • 0-day-analytics/tags/4.7.0/classes/vendor/helpers/class-hook-parameter-renderer.php

    r3442473 r3451291  
    1313namespace ADVAN\Helpers;
    1414
     15use ADVAN\Entities\Hooks_Management_Entity;
     16
    1517// Exit if accessed directly.
    1618if ( ! defined( 'ABSPATH' ) ) {
     
    3739         */
    3840        public static function render_parameters( string $hook_name, string $parameters ): string {
    39             global $wpdb;
    40 
    4141            // Get hook definition from hooks_management table.
    42             $management_table = $wpdb->prefix . ADVAN_PREFIX . 'hooks_management';
    43 
    44             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    45             $hook_config = $wpdb->get_row(
    46                 $wpdb->prepare(
    47                     "SELECT hook_parameters FROM `{$management_table}` WHERE hook_name = %s LIMIT 1",
    48                     $hook_name
    49                 ),
    50                 ARRAY_A
    51             );
     42            $hook_parameters = Hooks_Management_Entity::get_hook_parameters( $hook_name );
    5243
    5344            // Decode captured parameters.
     
    5950            // Get parameter definitions.
    6051            $param_defs = array();
    61             if ( $hook_config && ! empty( $hook_config['hook_parameters'] ) ) {
    62                 $param_defs = json_decode( $hook_config['hook_parameters'], true );
     52            if ( ! empty( $hook_parameters ) ) {
     53                $param_defs = json_decode( $hook_parameters, true );
    6354                if ( ! is_array( $param_defs ) ) {
    6455                    $param_defs = array();
     
    146137                    }
    147138                }
     139            }
     140
     141            if ( \is_array( $value ) && ! \in_array( $type, array( 'array', 'object' ), true ) ) {
     142                $type = 'array';
    148143            }
    149144            // Override type if actual value type differs significantly.
     
    216211         *
    217212         * @throws \Throwable If code execution fails.
     213         * @throws \Exception If unsafe code is detected.
    218214         *
    219215         * @since 4.5.0
     
    564560            $bool_val = filter_var( $value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
    565561            if ( is_null( $bool_val ) ) {
    566                 return '<code>' . \esc_html( $value ) . '</code>';
     562                return '<code>' . \esc_html( (string) $value ) . '</code>';
    567563            }
    568564            return $bool_val ? '<span style="color: green;">✓ true</span>' : '<span style="color: #999;">✗ false</span>';
     
    586582                return '<code>' . \esc_html( $str ) . '</code>';
    587583            }
    588             return '<code>' . \esc_html( print_r( $value, true ) ) . '</code>';
     584            return '<code>' . \esc_html( print_r( (array) $value, true ) ) . '</code>';
    589585        }
    590586
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/class-fatals-list.php

    r3442115 r3451291  
    8181
    8282        /**
     83         * Maximum stack trace lines to display in details
     84         *
     85         * @var int
     86         *
     87         * @since 4.7.0
     88         */
     89        protected const MAX_STACK_TRACE_LINES = 50;
     90
     91        /**
    8392         * Holds the prepared options for speeding the process
    8493         *
     
    222231                    $plugin = -1;
    223232                } else {
    224                     $plugin = \sanitize_text_field( \wp_unslash( $_REQUEST['plugin'] ) );
     233                    $plugin_raw = \sanitize_text_field( \wp_unslash( $_REQUEST['plugin'] ) );
     234                    // Validate plugin slug format (alphanumeric, dashes, underscores, max 255 chars).
     235                    if ( preg_match( '/^[a-zA-Z0-9\-_]{1,255}$/', $plugin_raw ) ) {
     236                        $plugin = $plugin_raw;
     237                    } else {
     238                        $plugin = '';
     239                    }
    225240                }
    226241            } else {
     
    469484                            'action'   => 'log_source_view',
    470485                        );
     486                        $line_count = 0;
    471487                        foreach ( $reversed_details as $val ) {
     488                            if ( $line_count >= self::MAX_STACK_TRACE_LINES ) {
     489                                $message .= '<br>... (' . ( count( $reversed_details ) - $line_count ) . ' more lines truncated)';
     490                                break;
     491                            }
    472492
    473493                            $source_link = '';
     
    503523
    504524                            $message = \rtrim( $message, ' - ' );
     525                            $line_count++;
    505526                        }
    506527                        $message .= '</pre></div>';
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/class-hooks-capture-list.php

    r3448917 r3451291  
    746746                    $hook_name  = '<code>' . \esc_html( $item[ $column_name ] ) . '</code>';
    747747
    748                     // Make hook name a link to hooks management if hooks_management_id is available
     748                    // Make hook name a link to hooks management if hooks_management_id is available.
    749749                    if ( ! empty( $item['hooks_management_id'] ) ) {
    750750                        $edit_url  = \network_admin_url( 'admin.php?page=advan_hooks_management&action=edit&id=' . absint( $item['hooks_management_id'] ) );
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/class-hooks-management-list.php

    r3442115 r3451291  
    2222use ADVAN\Entities_Global\Common_Table;
    2323use ADVAN\Entities\Hooks_Management_Entity;
     24use ADVAN\Entities\Hook_Groups_Entity;
    2425use ADVAN\Lists\Views\Hooks_Management_View;
    2526
     
    452453         */
    453454        public function get_bulk_actions() {
    454             return array(
     455            $actions = array(
    455456                'delete'  => \esc_html__( 'Delete', '0-day-analytics' ),
    456457                'enable'  => \esc_html__( 'Enable', '0-day-analytics' ),
    457458                'disable' => \esc_html__( 'Disable', '0-day-analytics' ),
    458459            );
     460
     461            // Add group assignment actions.
     462            $groups = Hook_Groups_Entity::get_groups_array();
     463            if ( ! empty( $groups ) ) {
     464                foreach ( $groups as $group_id => $group ) {
     465                    $actions[ 'assign_group_' . $group_id ] = sprintf(
     466                        /* translators: %s: Group name */
     467                        \esc_html__( 'Assign to group: %s', '0-day-analytics' ),
     468                        $group['name']
     469                    );
     470                }
     471            }
     472
     473            return $actions;
    459474        }
    460475
     
    539554
    540555            // Handle bulk actions.
    541             if ( in_array( $action, array( 'delete', 'enable', 'disable' ), true ) ) {
     556            if ( in_array( $action, array( 'delete', 'enable', 'disable' ), true ) || ( is_string( $action ) && strpos( $action, 'assign_group_' ) === 0 ) ) {
    542557                $nonce = isset( $_REQUEST['_wpnonce'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
    543558
     
    550565
    551566                if ( ! empty( $ids ) ) {
    552                     global $wpdb;
    553                     $table = Hooks_Management_Entity::get_table_name();
    554 
    555567                    foreach ( $ids as $id ) {
    556568                        if ( 'delete' === $action ) {
    557569                            Hooks_Management_Entity::delete_by_id( $id );
    558570                        } elseif ( 'enable' === $action ) {
    559                             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    560                             $wpdb->query(
    561                                 $wpdb->prepare(
    562                                     "UPDATE `{$table}` SET enabled = 1, date_modified = %f WHERE id = %d",
    563                                     microtime( true ),
    564                                     $id
    565                                 )
    566                             );
     571                            Hooks_Management_Entity::set_enabled( $id, true );
    567572                        } elseif ( 'disable' === $action ) {
    568                             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    569                             $wpdb->query(
    570                                 $wpdb->prepare(
    571                                     "UPDATE `{$table}` SET enabled = 0, date_modified = %f WHERE id = %d",
    572                                     microtime( true ),
    573                                     $id
    574                                 )
    575                             );
     573                            Hooks_Management_Entity::set_enabled( $id, false );
     574                        } elseif ( is_string( $action ) && strpos( $action, 'assign_group_' ) === 0 ) {
     575                            // Extract group ID from action.
     576                            $group_id = str_replace( 'assign_group_', '', $action );
     577                            $group_id = absint( $group_id );
     578
     579                            // Update the group_id for this hook.
     580                            Hooks_Management_Entity::set_group_id( $id, $group_id );
    576581                        }
    577582                    }
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/class-logs-list.php

    r3442115 r3451291  
    365365                    $result = Reverse_Line_Reader::read_file_from_end(
    366366                        $file,
    367                         function( $line, $pos ) use ( &$collected_items, &$errors, &$position ) {
     367                        function( $line, $pos ) use ( &$collected_items, &$errors, &$position, $items, $write_temp ) {
    368368
    369369                            $position = $pos;
     370
     371                            // Prevent memory exhaustion from extremely large multi-line errors.
     372                            if ( count( $collected_items ) > 500 ) { // Reasonable limit for stack traces.
     373                                $collected_items = array_slice( $collected_items, -100 ); // Keep last 100 items.
     374                                // if ( function_exists( 'error_log' ) ) {
     375                                //  error_log( 'Logs_List: Truncated collected_items due to size limit (' . count( $collected_items ) . ' items)' );
     376                                // }
     377                            }
    370378
    371379                            // Flag that holds the status of the error - are there more lines to read or not.
     
    387395                                    $errors[]      = $parsed_data;
    388396                                    $more_to_error = false;
     397
     398                                    // Prevent memory exhaustion from too many errors.
     399                                    if ( count( $errors ) >= $items * 2 ) { // Allow some buffer.
     400                                        return array(
     401                                            'close'     => true,
     402                                            'line_done' => true,
     403                                            'no_flush'  => false,
     404                                        );
     405                                    }
    389406                                } elseif ( \is_array( $parsed_data ) ) {
    390407                                    if ( isset( $parsed_data['call'] ) && str_starts_with( trim( $parsed_data['call'] ), 'made by' ) ) {
     
    12331250                            }
    12341251                            ?>
    1235                                 <option <?php echo ( $selected ); ?> value="<?php echo \esc_attr( $name ); ?>"><?php echo \esc_attr( $name );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></option>
     1252                                <option <?php echo ( $selected ); ?> value="<?php echo \esc_attr( $name ); ?>"><?php echo \esc_html( $name ); ?></option>
    12361253                                <?php
    12371254                        }
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/class-table-list.php

    r3442115 r3451291  
    643643                   
    644644                </select>
     645                <?php if ( 'top' === $which ) : ?>
     646                <button id="table-info-btn" class="button button-secondary" title="<?php esc_attr_e( 'View table information', '0-day-analytics' ); ?>">
     647                    <span class="dashicons dashicons-info"></span>
     648                    <?php esc_html_e( 'Info', '0-day-analytics' ); ?>
     649                </button>
     650                <?php endif; ?>
    645651               
    646652            </div>
     
    667673                <?php
    668674            }
     675
     676            // Table info modal - only for top navigation
     677            if ( 'top' === $which ) :
     678            ?>
     679            <div id="table-info-modal" class="table-info-modal" style="display: none;">
     680                <div class="table-info-modal-content">
     681                    <div class="table-info-modal-header">
     682                        <h3><?php printf( esc_html__( 'Table Information: %s', '0-day-analytics' ), esc_html( self::$table::get_name() ) ); ?></h3>
     683                        <button type="button" class="table-info-modal-close">&times;</button>
     684                    </div>
     685                    <div class="table-info-modal-body">
     686                        <div class="table-info-loading">
     687                            <span class="spinner is-active"></span>
     688                            <?php esc_html_e( 'Loading table information...', '0-day-analytics' ); ?>
     689                        </div>
     690                        <div class="table-info-content" style="display: none;">
     691                            <div class="table-info-section">
     692                                <h4><?php esc_html_e( 'Table Structure', '0-day-analytics' ); ?></h4>
     693                                <div class="table-structure-info"></div>
     694                            </div>
     695                            <div class="table-info-section">
     696                                <h4><?php esc_html_e( 'Indexes', '0-day-analytics' ); ?></h4>
     697                                <div class="table-indexes-info"></div>
     698                            </div>
     699                            <div class="table-info-section">
     700                                <h4><?php esc_html_e( 'Foreign Keys', '0-day-analytics' ); ?></h4>
     701                                <div class="table-foreign-keys-info"></div>
     702                            </div>
     703                            <div class="table-info-section">
     704                                <h4><?php esc_html_e( 'Table Health', '0-day-analytics' ); ?></h4>
     705                                <div class="table-health-info"></div>
     706                            </div>
     707                        </div>
     708                    </div>
     709                </div>
     710            </div>
     711            <?php endif; ?>
     712
     713            <?php
    669714            // if ( 'top' === $which ) {
    670715            global $wpdb;
     
    836881                makeSearchableDropdown(document.getElementById("table_filter_<?php echo \esc_attr( $which ); ?>"));
    837882
     883                <?php if ( 'top' === $which ) { ?>
     884                // Table info modal functionality
     885                const tableInfoBtn = document.getElementById('table-info-btn');
     886                const tableInfoModal = document.getElementById('table-info-modal');
     887                const tableInfoClose = document.querySelector('.table-info-modal-close');
     888
     889                if (tableInfoBtn && tableInfoModal) {
     890                    tableInfoBtn.addEventListener('click', function(e) {
     891                        e.preventDefault();
     892                        loadTableInfo();
     893                        tableInfoModal.style.display = 'block';
     894                    });
     895
     896                    tableInfoClose.addEventListener('click', function() {
     897                        tableInfoModal.style.display = 'none';
     898                    });
     899
     900                    window.addEventListener('click', function(e) {
     901                        if (e.target === tableInfoModal) {
     902                            tableInfoModal.style.display = 'none';
     903                        }
     904                    });
     905                }
     906
     907                function loadTableInfo() {
     908                    const loadingEl = document.querySelector('.table-info-loading');
     909                    const contentEl = document.querySelector('.table-info-content');
     910
     911                    loadingEl.style.display = 'block';
     912                    contentEl.style.display = 'none';
     913
     914                    fetch('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', {
     915                        method: 'POST',
     916                        headers: {
     917                            'Content-Type': 'application/x-www-form-urlencoded',
     918                        },
     919                        body: new URLSearchParams({
     920                            action: 'aadvana_get_table_info',
     921                            table_name: '<?php echo esc_js(self::$table::get_name()); ?>',
     922                            security: '<?php echo wp_create_nonce('get_table_info'); ?>'
     923                        })
     924                    })
     925                    .then(response => response.json())
     926                    .then(data => {
     927                        loadingEl.style.display = 'none';
     928                        if (data.success) {
     929                            displayTableInfo(data.data);
     930                            contentEl.style.display = 'block';
     931                        } else {
     932                            alert('<?php esc_html_e('Error loading table information', '0-day-analytics'); ?>: ' + data.data);
     933                        }
     934                    })
     935                    .catch(error => {
     936                        loadingEl.style.display = 'none';
     937                        alert('<?php esc_html_e('Error loading table information', '0-day-analytics'); ?>: ' + error.message);
     938                    });
     939                }
     940
     941                function displayTableInfo(info) {
     942                    // Table Structure
     943                    const structureEl = document.querySelector('.table-structure-info');
     944                    if (info.structure && info.structure.length > 0) {
     945                        let html = '<table class="widefat striped"><thead><tr><th><?php esc_html_e('Column', '0-day-analytics'); ?></th><th><?php esc_html_e('Type', '0-day-analytics'); ?></th><th><?php esc_html_e('Null', '0-day-analytics'); ?></th><th><?php esc_html_e('Key', '0-day-analytics'); ?></th><th><?php esc_html_e('Default', '0-day-analytics'); ?></th><th><?php esc_html_e('Extra', '0-day-analytics'); ?></th></tr></thead><tbody>';
     946                        info.structure.forEach(col => {
     947                            html += `<tr>
     948                                <td><code>${escapeHtml(col.Field)}</code></td>
     949                                <td><code>${escapeHtml(col.Type)}</code></td>
     950                                <td>${escapeHtml(col.Null)}</td>
     951                                <td>${escapeHtml(col.Key || '')}</td>
     952                                <td>${escapeHtml(col.Default || '')}</td>
     953                                <td>${escapeHtml(col.Extra || '')}</td>
     954                            </tr>`;
     955                        });
     956                        html += '</tbody></table>';
     957                        structureEl.innerHTML = html;
     958                    } else {
     959                        structureEl.innerHTML = '<p><?php esc_html_e('No column information available', '0-day-analytics'); ?></p>';
     960                    }
     961
     962                    // Indexes
     963                    const indexesEl = document.querySelector('.table-indexes-info');
     964                    if (info.indexes && info.indexes.length > 0) {
     965                        let html = '<table class="widefat striped"><thead><tr><th><?php esc_html_e('Key Name', '0-day-analytics'); ?></th><th><?php esc_html_e('Column', '0-day-analytics'); ?></th><th><?php esc_html_e('Unique', '0-day-analytics'); ?></th><th><?php esc_html_e('Type', '0-day-analytics'); ?></th></tr></thead><tbody>';
     966                        info.indexes.forEach(idx => {
     967                            html += `<tr>
     968                                <td><code>${escapeHtml(idx.Key_name)}</code></td>
     969                                <td><code>${escapeHtml(idx.Column_name)}</code></td>
     970                                <td>${idx.Non_unique === '0' ? '<?php esc_html_e('Yes', '0-day-analytics'); ?>' : '<?php esc_html_e('No', '0-day-analytics'); ?>'}</td>
     971                                <td>${escapeHtml(idx.Index_type)}</td>
     972                            </tr>`;
     973                        });
     974                        html += '</tbody></table>';
     975                        indexesEl.innerHTML = html;
     976                    } else {
     977                        indexesEl.innerHTML = '<p><?php esc_html_e('No indexes found', '0-day-analytics'); ?></p>';
     978                    }
     979
     980                    // Foreign Keys
     981                    const fkEl = document.querySelector('.table-foreign-keys-info');
     982                    if (info.foreign_keys && info.foreign_keys.length > 0) {
     983                        let html = '<table class="widefat striped"><thead><tr><th><?php esc_html_e('Constraint', '0-day-analytics'); ?></th><th><?php esc_html_e('Column', '0-day-analytics'); ?></th><th><?php esc_html_e('Referenced Table', '0-day-analytics'); ?></th><th><?php esc_html_e('Referenced Column', '0-day-analytics'); ?></th></tr></thead><tbody>';
     984                        info.foreign_keys.forEach(fk => {
     985                            html += `<tr>
     986                                <td><code>${escapeHtml(fk.constraint_name)}</code></td>
     987                                <td><code>${escapeHtml(fk.column_name)}</code></td>
     988                                <td><code>${escapeHtml(fk.referenced_table_name)}</code></td>
     989                                <td><code>${escapeHtml(fk.referenced_column_name)}</code></td>
     990                            </tr>`;
     991                        });
     992                        html += '</tbody></table>';
     993                        fkEl.innerHTML = html;
     994                    } else {
     995                        fkEl.innerHTML = '<p><?php esc_html_e('No foreign keys found', '0-day-analytics'); ?></p>';
     996                    }
     997
     998                    // Table Health
     999                    const healthEl = document.querySelector('.table-health-info');
     1000                    if (info.health) {
     1001                        let html = '<ul>';
     1002                        if (info.health.needs_optimization) {
     1003                            html += `<li><span style="color: #d63638;">⚠️ <?php esc_html_e('Table may benefit from optimization', '0-day-analytics'); ?></span></li>`;
     1004                        } else {
     1005                            html += `<li><span style="color: #00a32a;">✅ <?php esc_html_e('Table appears to be well optimized', '0-day-analytics'); ?></span></li>`;
     1006                        }
     1007                        if (info.health.row_count) {
     1008                            html += `<li><?php esc_html_e('Row count', '0-day-analytics'); ?>: ${info.health.row_count.toLocaleString()}</li>`;
     1009                        }
     1010                        if (info.health.data_size) {
     1011                            html += `<li><?php esc_html_e('Data size', '0-day-analytics'); ?>: ${formatBytes(info.health.data_size)}</li>`;
     1012                        }
     1013                        if (info.health.index_size) {
     1014                            html += `<li><?php esc_html_e('Index size', '0-day-analytics'); ?>: ${formatBytes(info.health.index_size)}</li>`;
     1015                        }
     1016                        html += '</ul>';
     1017                        healthEl.innerHTML = html;
     1018                    } else {
     1019                        healthEl.innerHTML = '<p><?php esc_html_e('Health information not available', '0-day-analytics'); ?></p>';
     1020                    }
     1021                }
     1022
     1023                function escapeHtml(text) {
     1024                    const div = document.createElement('div');
     1025                    div.textContent = text;
     1026                    return div.innerHTML;
     1027                }
     1028
     1029                function formatBytes(bytes) {
     1030                    if (bytes === 0) return '0 Bytes';
     1031                    const k = 1024;
     1032                    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
     1033                    const i = Math.floor(Math.log(bytes) / Math.log(k));
     1034                    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
     1035                }
     1036                <?php } ?>
     1037
    8381038            </script>
    8391039                        <?php
     
    9401140                        top: -9999px;
    9411141                        }
     1142
     1143                        /* Table Info Modal Styles */
     1144                        .table-info-modal {
     1145                        position: fixed;
     1146                        z-index: 100000;
     1147                        left: 0;
     1148                        top: 0;
     1149                        width: 100%;
     1150                        height: 100%;
     1151                        background-color: rgba(0, 0, 0, 0.5);
     1152                        }
     1153
     1154                        .table-info-modal-content {
     1155                        background-color: #fefefe;
     1156                        margin: 5% auto;
     1157                        padding: 0;
     1158                        border: 1px solid #888;
     1159                        width: 90%;
     1160                        max-width: 1000px;
     1161                        max-height: 80vh;
     1162                        overflow-y: auto;
     1163                        border-radius: 8px;
     1164                        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
     1165                        }
     1166
     1167                        .aadvana-darkskin .table-info-modal-content {
     1168                        background-color: #1d456b !important;
     1169                        border-color: #555;
     1170                        }
     1171
     1172                        .table-info-modal-header {
     1173                        padding: 15px 20px;
     1174                        /* background: #f8f9fa; */
     1175                        border-bottom: 1px solid #dee2e6;
     1176                        display: flex;
     1177                        justify-content: space-between;
     1178                        align-items: center;
     1179                        border-radius: 8px 8px 0 0;
     1180                        }
     1181
     1182                        .table-info-modal-header h3 {
     1183                        margin: 0;
     1184                        /* color: #333; */
     1185                        font-size: 18px;
     1186                        font-weight: 600;
     1187                        }
     1188
     1189                        .table-info-modal-close {
     1190                        background: none;
     1191                        border: none;
     1192                        font-size: 24px;
     1193                        font-weight: bold;
     1194                        /* color: #666; */
     1195                        cursor: pointer;
     1196                        padding: 0;
     1197                        width: 30px;
     1198                        height: 30px;
     1199                        display: flex;
     1200                        align-items: center;
     1201                        justify-content: center;
     1202                        border-radius: 50%;
     1203                        transition: background-color 0.2s;
     1204                        }
     1205
     1206                        .table-info-modal-close:hover {
     1207                        /* background-color: #e9ecef; */
     1208                        /* color: #333; */
     1209                        }
     1210
     1211                        .table-info-modal-body {
     1212                        padding: 20px;
     1213                        }
     1214
     1215                        .table-info-loading {
     1216                        text-align: center;
     1217                        padding: 40px;
     1218                        }
     1219
     1220                        .table-info-loading .spinner {
     1221                        float: none;
     1222                        margin: 0 auto 10px;
     1223                        }
     1224
     1225                        .table-info-section {
     1226                        margin-bottom: 30px;
     1227                        }
     1228
     1229                        .table-info-section h4 {
     1230                        margin: 0 0 15px 0;
     1231                        padding: 10px 15px;
     1232                        /* background: #f8f9fa; */
     1233                        border-left: 4px solid #007cba;
     1234                        font-size: 16px;
     1235                        font-weight: 600;
     1236                        /* color: #333; */
     1237                        }
     1238
     1239                        .table-info-section table {
     1240                        margin: 0;
     1241                        }
     1242
     1243                        .table-info-section table code {
     1244                        /* background: #f8f9fa; */
     1245                        padding: 2px 6px;
     1246                        border-radius: 3px;
     1247                        font-size: 12px;
     1248                        /* color: #495057; */
     1249                        }
     1250
     1251                        .table-info-section ul {
     1252                        margin: 0;
     1253                        padding-left: 20px;
     1254                        }
     1255
     1256                        .table-info-section li {
     1257                        margin-bottom: 8px;
     1258                        }
     1259
     1260                        @media (max-width: 768px) {
     1261                        .table-info-modal-content {
     1262                            margin: 2% auto;
     1263                            width: 95%;
     1264                            max-height: 90vh;
     1265                        }
     1266
     1267                        .table-info-modal-body {
     1268                            padding: 15px;
     1269                        }
     1270
     1271                        .table-info-section table {
     1272                            font-size: 12px;
     1273                        }
     1274
     1275                        .table-info-section table th,
     1276                        .table-info-section table td {
     1277                            padding: 8px 4px;
     1278                        }
     1279                        }
    9421280                    </style>
    9431281                        <?php } ?>
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/entity/class-common-table.php

    r3442115 r3451291  
    20812081            }
    20822082        }
     2083
     2084        /**
     2085         * Get detailed table information including structure, indexes, foreign keys, and health.
     2086         *
     2087         * @param string $table_name The table name to get information for.
     2088         * @param \wpdb  $connection Optional database connection.
     2089         *
     2090         * @return array|\WP_Error Array of table information or WP_Error on failure.
     2091         *
     2092         * @since 4.7.0
     2093         */
     2094        public static function get_table_info( string $table_name, $connection = null ) {
     2095            if ( null !== $connection ) {
     2096                if ( $connection instanceof \wpdb ) {
     2097                    $_wpdb = $connection;
     2098                } else {
     2099                    global $wpdb;
     2100                    $_wpdb = $wpdb;
     2101                }
     2102            } else {
     2103                global $wpdb;
     2104                $_wpdb = $wpdb;
     2105            }
     2106
     2107            // Validate table name.
     2108            if ( ! self::validate_table_name( $table_name ) ) {
     2109                return new \WP_Error( 'invalid_table', 'Invalid table name.' );
     2110            }
     2111
     2112            // Check if table exists.
     2113            if ( ! self::check_table_exists( $table_name, $_wpdb ) ) {
     2114                return new \WP_Error( 'table_not_found', 'Table does not exist.' );
     2115            }
     2116
     2117            $table_info = array();
     2118
     2119            try {
     2120                // Get table structure (columns) - using DESC command like other methods.
     2121                $table_info['structure'] = $_wpdb->get_results(
     2122                    $_wpdb->prepare( 'DESC %i', $table_name ),
     2123                    ARRAY_A
     2124                );
     2125
     2126                // Get indexes - using SHOW INDEX command.
     2127                $table_info['indexes'] = $_wpdb->get_results(
     2128                    $_wpdb->prepare( 'SHOW INDEX FROM %i', $table_name ),
     2129                    ARRAY_A
     2130                );
     2131
     2132                // Get foreign keys from information_schema - using prepared query.
     2133                $table_info['foreign_keys'] = $_wpdb->get_results(
     2134                    $_wpdb->prepare(
     2135                        'SELECT
     2136                            kcu.constraint_name,
     2137                            kcu.column_name,
     2138                            kcu.referenced_table_name,
     2139                            kcu.referenced_column_name
     2140                        FROM information_schema.key_column_usage kcu
     2141                        JOIN information_schema.table_constraints tc
     2142                            ON kcu.constraint_name = tc.constraint_name
     2143                            AND kcu.table_schema = tc.table_schema
     2144                        WHERE kcu.table_schema = %s
     2145                            AND kcu.table_name = %s
     2146                            AND tc.constraint_type = %s',
     2147                        $_wpdb->dbname,
     2148                        $table_name,
     2149                        'FOREIGN KEY'
     2150                    ),
     2151                    ARRAY_A
     2152                );
     2153
     2154                // Get table health information from information_schema - using prepared query.
     2155                $health_info = $_wpdb->get_row(
     2156                    $_wpdb->prepare(
     2157                        'SELECT
     2158                            table_rows as row_count,
     2159                            data_length as data_size,
     2160                            index_length as index_size,
     2161                            data_free as fragmentation
     2162                        FROM information_schema.tables
     2163                        WHERE table_schema = %s AND table_name = %s',
     2164                        $_wpdb->dbname,
     2165                        $table_name
     2166                    ),
     2167                    ARRAY_A
     2168                );
     2169
     2170                $table_info['health'] = array(
     2171                    'row_count' => $health_info ? (int) $health_info['row_count'] : 0,
     2172                    'data_size' => $health_info ? (int) $health_info['data_size'] : 0,
     2173                    'index_size' => $health_info ? (int) $health_info['index_size'] : 0,
     2174                    'needs_optimization' => $health_info && (int) $health_info['fragmentation'] > 0,
     2175                );
     2176
     2177            } catch ( \Exception $e ) {
     2178                return new \WP_Error( 'database_error', 'Database error: ' . $e->getMessage() );
     2179            }
     2180
     2181            return $table_info;
     2182        }
    20832183    }
    20842184}
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/views/class-hooks-management-view.php

    r3442115 r3451291  
    172172
    173173            if ( $id ) {
    174                 global $wpdb;
    175                 $table = Hooks_Management_Entity::get_table_name();
    176 
    177                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    178                 $hook = $wpdb->get_row(
    179                     $wpdb->prepare( 'SELECT * FROM `' . $table . '` WHERE id = %d', $id ),
    180                     ARRAY_A
    181                 );
     174                $hook = Hooks_Management_Entity::load( 'id = %d', array( $id ) );
    182175
    183176                if ( ! $hook ) {
  • 0-day-analytics/tags/4.7.0/classes/vendor/lists/views/class-logs-list-view.php

    r3391413 r3451291  
    3131     */
    3232    class Logs_List_View {
    33 
    3433        /**
    3534         * Displays the settings page.
     
    5958            <?php
    6059
    61             $plugin_filter = $_REQUEST['plugin_filter'] ?? ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     60            // Verify nonce for security
     61            if ( isset( $_REQUEST['advanced-analytics-security'] ) && \wp_verify_nonce( \sanitize_key( \wp_unslash( $_REQUEST['advanced-analytics-security'] ) ), 'advan-plugin-data' ) ) {
     62                $plugin_filter = isset( $_REQUEST['plugin_filter'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['plugin_filter'] ) ) : '';
     63            } else {
     64                $plugin_filter = '';
     65            }
    6266
    63             $plugin_filter = \sanitize_text_field( \wp_unslash( $plugin_filter ) );
    64             $events_list   = new Logs_List(
     67            $events_list = new Logs_List(
    6568                array(
    6669                    'plugin_filter' => $plugin_filter,
     
    103106            }
    104107
    105             if ( ! empty( $_GET['single_severity_filter_top'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified via WP_Helper::verify_admin_nonce below.
     108            if ( ! empty( $_GET['single_severity_filter_top'] ) ) {
    106109                WP_Helper::verify_admin_nonce( 'advan-plugin-data', 'advanced-analytics-security' );
    107110
    108111                // Validate and strictly compare plugin filter against known plugin bases.
    109                 if ( isset( $_GET['plugin_filter'] ) && '' !== $_GET['plugin_filter'] && -1 !== (int) $_GET['plugin_filter'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above.
    110                     $raw_plugin_filter = \sanitize_text_field( \wp_unslash( (string) $_GET['plugin_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above.
     112                if ( isset( $_GET['plugin_filter'] ) && '' !== $_GET['plugin_filter'] && -1 !== (int) $_GET['plugin_filter'] ) {
     113                    $raw_plugin_filter = \sanitize_text_field( \wp_unslash( (string) $_GET['plugin_filter'] ) );
    111114                    if ( ! \in_array( $raw_plugin_filter, Plugin_Theme_Helper::get_plugins_bases(), true ) ) {
    112                         \wp_safe_redirect(
    113                             \remove_query_arg(
    114                                 array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action', 'plugin_filter' ),
    115                                 isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''
    116                             )
    117                         );
    118                         exit;
     115                        if ( ! \in_array( $raw_plugin_filter, Plugin_Theme_Helper::get_plugins_bases(), true ) ) {
     116                            \wp_safe_redirect(
     117                                \remove_query_arg(
     118                                    array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action', 'plugin_filter' ),
     119                                    isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''
     120                                )
     121                            );
     122                            exit;
     123                        }
    119124                    }
     125
     126                    \wp_safe_redirect(
     127                        \remove_query_arg( array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action' ), isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' )
     128                    );
     129                    exit;
    120130                }
    121 
    122                 \wp_safe_redirect(
    123                     \remove_query_arg( array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action' ), isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' )
    124                 );
    125                 exit;
    126131            }
    127132        }
  • 0-day-analytics/tags/4.7.0/readme.txt

    r3448917 r3451291  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 4.6.0
     7Stable tag: 4.7.0
    88License: GPLv3 or later
    99License URI: https://www.gnu.org/licenses/gpl-3.0.txt
     
    9393== Changelog ==
    9494
     95= 4.7.0 =
     96* Hooks module improvements - group assignment in bulk. Bug fixes and code optimizations.
     97
    9598= 4.6.0 =
    9699* Hooks module improvements and small styling issues fixed.
  • 0-day-analytics/trunk/advanced-analytics.php

    r3448917 r3451291  
    1111 * Plugin Name:     0 Day Analytics
    1212 * Description:     Take full control of error log, crons, transients, plugins, requests, mails and DB tables.
    13  * Version:         4.6.0
     13 * Version:         4.7.0
    1414 * Author:          Stoil Dobrev
    1515 * Author URI:      https://github.com/sdobreff/
     
    3939// Constants.
    4040if ( ! defined( 'ADVAN_VERSION' ) ) {
    41     define( 'ADVAN_VERSION', '4.6.0' );
     41    define( 'ADVAN_VERSION', '4.7.0' );
    4242    define( 'ADVAN_TEXTDOMAIN', '0-day-analytics' );
    4343    define( 'ADVAN_NAME', '0 Day Analytics' );
  • 0-day-analytics/trunk/classes/migration/class-migration.php

    r3448917 r3451291  
    1414
    1515use ADVAN\Helpers\Settings;
     16use ADVAN\Entities\WP_Mail_Entity;
     17use ADVAN\Entities\WP_Fatals_Entity;
     18use ADVAN\Entities\Requests_Log_Entity;
    1619use ADVAN\Entities_Global\Common_Table;
    17 use ADVAN\Entities\WP_Mail_Entity;
    18 use ADVAN\Entities\Requests_Log_Entity;
    1920use ADVAN\Migration\Abstract_Migration;
    2021
     
    267268            }
    268269        }
     270
     271        /**
     272         * Migrates the plugin up-to version 4.7.0 (adds performance indexes to fatals table).
     273         *
     274         * @return void
     275         *
     276         * @since 4.7.0
     277         */
     278        public static function migrate_up_to_470() {
     279            if ( \class_exists( '\\ADVAN\\Entities\\WP_Fatals_Entity' ) ) {
     280                if ( Common_Table::check_table_exists( WP_Fatals_Entity::get_table_name() ) ) {
     281                    WP_Fatals_Entity::alter_table_470();
     282                }
     283            }
     284        }
    269285    }
    270286}
  • 0-day-analytics/trunk/classes/vendor/controllers/class-hooks-capture.php

    r3448917 r3451291  
    192192
    193193            // In WP-CLI context, ensure hooks are attached properly.
    194             if ( defined( 'WP_CLI' ) && WP_CLI ) {
     194            if ( defined( 'WP_CLI' ) && \WP_CLI ) {
    195195                self::attach_hooks_cli();
    196196            } else {
  • 0-day-analytics/trunk/classes/vendor/controllers/class-reverse-line-reader.php

    r3393178 r3451291  
    2828
    2929        /**
     30         * Memory limit in MB before triggering cleanup or termination
     31         */
     32        const MEMORY_LIMIT_MB = 32;
     33
     34        /**
     35         * Time limit in seconds for processing
     36         */
     37        const TIME_LIMIT_SECONDS = 25;
     38
     39        /**
     40         * Maximum number of collected items to prevent memory exhaustion
     41         */
     42        const MAX_COLLECTED_ITEMS = 1000;
     43
     44        /**
    3045         * Keeps track of of the current position in the file.
    3146         *
     
    8095         */
    8196        private static $memory_handle = null;
     97
     98        /**
     99         * Stores the overflow file handle for spilling data to disk when memory is critical.
     100         *
     101         * @var resource|null
     102         *
     103         * @since 1.9.3
     104         */
     105        private static $overflow_handle = null;
    82106
    83107        /**
     
    146170                }
    147171
     172                // Adaptive buffer sizing based on available memory.
     173                $available_memory = self::get_available_memory_mb();
     174                if ( $available_memory < 50 ) { // Low memory system.
     175                    self::$buffer_size = min( self::$buffer_size, 4096 ); // Use smaller buffer.
     176                } elseif ( $available_memory < 100 ) { // Medium memory system.
     177                    self::$buffer_size = min( self::$buffer_size, 8192 ); // Use medium buffer.
     178                }
     179                // Otherwise use default BUFFER_SIZE.
     180
    148181                self::$file_size = - (int) $size;
    149182            }
     183
     184            // Initialize memory and time monitoring.
     185            $start_time      = time();
     186            $start_memory    = memory_get_usage( true );
     187            $processed_lines = 0;
    150188
    151189            $line = self::readline();
     
    165203            }
    166204            $result = $callback( $line, self::$pos );
     205
     206            // Memory and time safety checks.
     207            ++$processed_lines;
     208            $current_memory_mb = ( memory_get_usage( true ) - $start_memory ) / 1024 / 1024;
     209            $elapsed_time      = time() - $start_time;
     210
     211            // Terminate if memory limit exceeded.
     212            if ( $current_memory_mb > self::MEMORY_LIMIT_MB ) {
     213                if ( function_exists( 'error_log' ) ) {
     214                    error_log( 'Reverse_Line_Reader: Memory limit exceeded (' . round( $current_memory_mb, 2 ) . 'MB), terminating processing' );
     215                }
     216                self::reset_class_globals();
     217                $max_lines = 0;
     218                return false;
     219            }
     220
     221            // Create overflow file if memory is getting critical (80% of limit).
     222            if ( $current_memory_mb > ( self::MEMORY_LIMIT_MB * 0.8 ) && null === self::$overflow_handle ) {
     223                self::$overflow_handle = self::create_overflow_temp_file();
     224                if ( self::$overflow_handle && function_exists( 'error_log' ) ) {
     225                    error_log( 'Reverse_Line_Reader: Created overflow file due to high memory usage (' . round( $current_memory_mb, 2 ) . 'MB)' );
     226                }
     227            }
     228
     229            // Terminate if time limit exceeded.
     230            if ( $elapsed_time > self::TIME_LIMIT_SECONDS ) {
     231                if ( function_exists( 'error_log' ) ) {
     232                    error_log( 'Reverse_Line_Reader: Time limit exceeded (' . $elapsed_time . 's), terminating processing' );
     233                }
     234                self::reset_class_globals();
     235                $max_lines = 0;
     236                return false;
     237            }
    167238
    168239            if ( true === $result['close'] ) {
     
    283354         */
    284355        public static function write_memory_file( string $line ) {
     356            // If overflow file is active, write to it instead of memory.
     357            if ( null !== self::$overflow_handle ) {
     358                fwrite( self::$overflow_handle, $line ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
     359                return;
     360            }
     361
    285362            if ( null === self::$memory_handle ) {
    286363                self::$memory_handle = fopen( 'php://memory', 'w+' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
     
    301378                rewind( self::$temp_handle ); // resets the position of pointer.
    302379
     380                // Content is pre-escaped with esc_html() before being written to temp file
    303381                echo fread( self::$temp_handle, fstat( self::$temp_handle )['size'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
    304382
     
    315393         */
    316394        public static function read_memory_file() {
    317             if ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
     395            // If overflow file exists, read from it instead.
     396            if ( \is_resource( self::$overflow_handle ) && ( 'handle' === get_resource_type( self::$overflow_handle ) || 'stream' === get_resource_type( self::$overflow_handle ) ) ) {
     397                rewind( self::$overflow_handle ); // resets the position of pointer.
     398
     399                // Content is pre-escaped with esc_html() before being written to overflow file.
     400                echo fread( self::$overflow_handle, fstat( self::$overflow_handle )['size'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
     401
     402                fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     403                self::$overflow_handle = null;
     404            } elseif ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
    318405                rewind( self::$memory_handle ); // resets the position of pointer.
    319406
     407                // Content is pre-escaped with esc_html() before being written to memory file.
    320408                echo fread( self::$memory_handle, fstat( self::$memory_handle )['size'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
    321409
     
    334422         */
    335423        public static function flush_memory_file_to_temp() {
    336             if ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
    337 
     424            // Handle overflow file first if it exists.
     425            if ( \is_resource( self::$overflow_handle ) && ( 'handle' === get_resource_type( self::$overflow_handle ) || 'stream' === get_resource_type( self::$overflow_handle ) ) ) {
    338426                $line = '';
    339                 for ( $x_pos = 0; fseek( self::$memory_handle, $x_pos, SEEK_END ) !== -1; $x_pos-- ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
    340                     $char = fgetc( self::$memory_handle );
     427                for ( $x_pos = 0; fseek( self::$overflow_handle, $x_pos, SEEK_END ) !== -1; $x_pos-- ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
     428                    $char = fgetc( self::$overflow_handle );
    341429
    342430                    if ( PHP_EOL === $char ) {
     
    351439                    self::write_temp_file( $line . PHP_EOL );
    352440                }
     441                fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     442                self::$overflow_handle = null;
     443            } elseif ( \is_resource( self::$memory_handle ) && ( 'handle' === get_resource_type( self::$memory_handle ) || 'stream' === get_resource_type( self::$memory_handle ) ) ) {
     444                $line = '';
     445                for ( $x_pos = 0; fseek( self::$memory_handle, $x_pos, SEEK_END ) !== -1; $x_pos-- ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
     446                    $char = fgetc( self::$memory_handle );
     447
     448                    if ( PHP_EOL === $char ) {
     449                        self::write_temp_file( $line . PHP_EOL );
     450                        $line = '';
     451                        continue;
     452                    } else {
     453                        $line = $char . $line;
     454                    }
     455                }
     456                if ( ! empty( $line ) ) {
     457                    self::write_temp_file( $line . PHP_EOL );
     458                }
    353459                fclose( self::$memory_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
    354 
    355460                self::$memory_handle = null;
    356461            }
     
    375480                self::$memory_handle = null;
    376481            }
     482            if ( \is_resource( self::$overflow_handle ) && ( 'handle' === get_resource_type( self::$overflow_handle ) || 'stream' === get_resource_type( self::$overflow_handle ) ) ) {
     483                fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     484
     485                self::$overflow_handle = null;
     486            }
    377487        }
    378488
     
    387497            if ( \is_resource( self::$error_log_handle ) && ( 'handle' === get_resource_type( self::$error_log_handle ) || 'stream' === get_resource_type( self::$error_log_handle ) ) ) {
    388498                \fclose( self::$error_log_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
     499            }
     500
     501            if ( \is_resource( self::$overflow_handle ) ) {
     502                \fclose( self::$overflow_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
    389503            }
    390504
     
    393507            self::$pos              = null;
    394508            self::$error_log_handle = null;
     509            self::$overflow_handle  = null;
     510        }
     511
     512        /**
     513         * Get available memory in MB
     514         *
     515         * @return int Available memory in MB
     516         *
     517         * @since latest
     518         */
     519        public static function get_available_memory_mb() {
     520            $memory_limit = ini_get( 'memory_limit' );
     521            if ( '-1' === $memory_limit ) {
     522                return 256; // Assume 256MB if no limit.
     523            }
     524
     525            $memory_limit_bytes = wp_convert_hr_to_bytes( $memory_limit );
     526            $current_usage      = memory_get_usage( true );
     527            $available          = max( 0, $memory_limit_bytes - $current_usage );
     528
     529            return (int) ( $available / 1024 / 1024 );
     530        }
     531
     532        /**
     533         * Check if available memory is below critical threshold
     534         *
     535         * @return bool True if memory is critical, false otherwise
     536         *
     537         * @since latest
     538         */
     539        public static function is_memory_critical() {
     540            $available_mb = self::get_available_memory_mb();
     541            return $available_mb < 10; // Less than 10MB available.
    395542        }
    396543
     
    451598            return self::$temp_handle;
    452599        }
     600
     601        /**
     602         * Create a temporary file for overflow data when memory is critical
     603         *
     604         * @return resource|false File handle or false on failure
     605         *
     606         * @since latest
     607         */
     608        public static function create_overflow_temp_file() {
     609            $temp_dir  = get_temp_dir();
     610            $temp_file = tempnam( $temp_dir, 'advana_overflow_' );
     611
     612            if ( $temp_file && is_writable( $temp_file ) ) {
     613                return fopen( $temp_file, 'w+' );
     614            }
     615
     616            return false;
     617        }
     618
     619        /**
     620         * Clean up temporary overflow files
     621         *
     622         * @return void
     623         *
     624         * @since latest
     625         */
     626        public static function cleanup_overflow_files() {
     627            $temp_dir = get_temp_dir();
     628            $pattern  = $temp_dir . '/advana_overflow_*';
     629
     630            foreach ( glob( $pattern ) as $file ) {
     631                if ( is_file( $file ) && filemtime( $file ) < time() - 3600 ) { // Older than 1 hour.
     632                    unlink( $file );
     633                }
     634            }
     635        }
    453636    }
    454637}
  • 0-day-analytics/trunk/classes/vendor/controllers/classes-snippets-controller.php

    r3413502 r3451291  
    141141            }
    142142        }
     143
    143144        /**
    144145         * Fetch and cache runtime snippets list.
     
    153154            }
    154155
    155             $wpdb  = Snippet_Entity::get_connection();
    156             $table = Snippet_Entity::get_table_name( $wpdb );
    157 
    158             $query = $wpdb->prepare(
    159                 'SELECT * FROM ' . $table . ' WHERE blog_id = %d AND status = %d AND type = %s',
    160                 \get_current_blog_id(),
    161                 Snippet_Entity::STATUS_ENABLED,
    162                 'php'
    163             );
    164 
    165             $wpdb->suppress_errors( true );
    166             $results = $wpdb->get_results( $query, ARRAY_A );
    167             if ( '' !== $wpdb->last_error ) {
    168                 if ( 1146 === Snippet_Entity::get_last_sql_error( $wpdb ) ) {
    169                     if ( Snippet_Entity::create_table( $wpdb ) ) {
    170                         $results = array();
    171                     }
    172                 }
    173             }
    174             $wpdb->suppress_errors( false );
    175 
    176             self::$runtime_snippets = is_array( $results ) ? $results : array();
     156            self::$runtime_snippets = Snippet_Entity::get_runtime_snippets();
    177157
    178158            return self::$runtime_snippets;
  • 0-day-analytics/trunk/classes/vendor/entities/class-hooks-management-entity.php

    r3442473 r3451291  
    24742474            }
    24752475
    2476             // Add the ID to the data for the update operation.
    2477             $data['id'] = $id;
    2478 
    2479             $result = self::insert( $data );
     2476            // Load current record.
     2477            $current = self::load( 'id = %d', $id );
     2478            if ( ! $current ) {
     2479                return false;
     2480            }
     2481
     2482            // Merge new data with existing data.
     2483            $current = array_merge( $current, $data );
     2484
     2485            $result = self::insert( $current );
    24802486
    24812487            // Clear cache.
    24822488            \wp_cache_delete( 'advan_enabled_hooks', 'advan' );
     2489            self::clear_hook_labels_cache();
    24832490
    24842491            return $result > 0;
     2492        }
     2493
     2494        /**
     2495         * Set enabled status for a hook.
     2496         *
     2497         * @param int  $id      Hook ID.
     2498         * @param bool $enabled Enabled status.
     2499         *
     2500         * @return bool
     2501         *
     2502         * @since 4.7.0
     2503         */
     2504        public static function set_enabled( int $id, bool $enabled ): bool {
     2505            return self::update(
     2506                $id,
     2507                array(
     2508                    'enabled'       => $enabled ? 1 : 0,
     2509                    'date_modified' => microtime( true ),
     2510                )
     2511            );
     2512        }
     2513
     2514        /**
     2515         * Set group ID for a hook.
     2516         *
     2517         * @param int $id       Hook ID.
     2518         * @param int $group_id Group ID.
     2519         *
     2520         * @return bool
     2521         *
     2522         * @since 4.7.0
     2523         */
     2524        public static function set_group_id( int $id, int $group_id ): bool {
     2525            return self::update(
     2526                $id,
     2527                array(
     2528                    'group_id'      => $group_id,
     2529                    'date_modified' => microtime( true ),
     2530                )
     2531            );
     2532        }
     2533
     2534        /**
     2535         * Get hook parameters by hook name.
     2536         *
     2537         * @param string $hook_name Hook name.
     2538         *
     2539         * @return string
     2540         *
     2541         * @since 4.7.0
     2542         */
     2543        public static function get_hook_parameters( string $hook_name ): string {
     2544            $hook = self::load( 'hook_name = %s', array( $hook_name ) );
     2545            return $hook && isset( $hook['hook_parameters'] ) ? $hook['hook_parameters'] : '';
    24852546        }
    24862547    }
  • 0-day-analytics/trunk/classes/vendor/entities/class-snippet-entity.php

    r3413502 r3451291  
    640640            );
    641641        }
     642
     643        /**
     644         * Get runtime snippets (enabled PHP snippets for current blog).
     645         *
     646         * @return array
     647         *
     648         * @since 4.3.0
     649         */
     650        public static function get_runtime_snippets(): array {
     651            $wpdb    = self::get_connection();
     652            $table   = self::get_table_name( $wpdb );
     653            $blog_id = \get_current_blog_id();
     654
     655            $query = $wpdb->prepare(
     656                'SELECT * FROM ' . $table . ' WHERE blog_id = %d AND status = %d AND type = %s',
     657                $blog_id,
     658                self::STATUS_ENABLED,
     659                'php'
     660            );
     661
     662            $wpdb->suppress_errors( true );
     663            $results = $wpdb->get_results( $query, ARRAY_A );
     664            if ( '' !== $wpdb->last_error ) {
     665                if ( 1146 === self::get_last_sql_error( $wpdb ) ) {
     666                    if ( self::create_table( $wpdb ) ) {
     667                        $results = array();
     668                    }
     669                }
     670            }
     671            $wpdb->suppress_errors( false );
     672
     673            return is_array( $results ) ? $results : array();
     674        }
     675
     676        /**
     677         * Get snippets with filters for AJAX listing.
     678         *
     679         * @param array $filters Array of filters (search, status, type, etc.).
     680         * @param int   $offset  Pagination offset.
     681         * @param int   $limit   Pagination limit.
     682         *
     683         * @return array
     684         *
     685         * @since 4.3.0
     686         */
     687        public static function get_snippets_with_filters( array $filters = array(), int $offset = 0, int $limit = 50 ): array {
     688            $wpdb    = self::get_connection();
     689            $table   = self::get_table_name( $wpdb );
     690            $blog_id = \get_current_blog_id();
     691
     692            $where    = array( 'blog_id = %d' );
     693            $bindings = array( $blog_id );
     694
     695            // Search filter
     696            if ( ! empty( $filters['search'] ) ) {
     697                $search = '%' . $wpdb->esc_like( $filters['search'] ) . '%';
     698                $where[] = '(name LIKE %s OR tags LIKE %s)';
     699                $bindings[] = $search;
     700                $bindings[] = $search;
     701            }
     702
     703            // Status filter
     704            if ( isset( $filters['status'] ) ) {
     705                $status_map = array(
     706                    'enabled'  => self::STATUS_ENABLED,
     707                    'disabled' => self::STATUS_DISABLED,
     708                    'trash'    => self::STATUS_TRASHED,
     709                );
     710
     711                if ( isset( $status_map[ $filters['status'] ] ) ) {
     712                    $where[] = 'status = %d';
     713                    $bindings[] = $status_map[ $filters['status'] ];
     714                } else {
     715                    $where[] = 'status >= %d';
     716                    $bindings[] = self::STATUS_DISABLED;
     717                }
     718            }
     719
     720            // Type filter
     721            if ( ! empty( $filters['type'] ) && in_array( $filters['type'], array_keys( self::get_supported_types() ), true ) ) {
     722                $where[] = 'type = %s';
     723                $bindings[] = $filters['type'];
     724            }
     725
     726            $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';
     727            $orderby   = 'updated_at';
     728            $order     = 'DESC';
     729
     730            $query = 'SELECT * FROM ' . $table . ' ' . $where_sql . ' ORDER BY ' . $orderby . ' ' . $order . ' LIMIT %d OFFSET %d';
     731            $bindings[] = $limit;
     732            $bindings[] = $offset;
     733
     734            $wpdb->suppress_errors( true );
     735            $results = $wpdb->get_results( $wpdb->prepare( $query, $bindings ), ARRAY_A );
     736            if ( '' !== $wpdb->last_error ) {
     737                if ( 1146 === self::get_last_sql_error( $wpdb ) ) {
     738                    if ( self::create_table( $wpdb ) ) {
     739                        $results = array();
     740                    }
     741                }
     742            }
     743            $wpdb->suppress_errors( false );
     744
     745            return is_array( $results ) ? $results : array();
     746        }
     747
     748        /**
     749         * Get total count of snippets with filters.
     750         *
     751         * @param array $filters Array of filters (search, status, type, etc.).
     752         *
     753         * @return int
     754         *
     755         * @since 4.3.0
     756         */
     757        public static function get_snippets_count_with_filters( array $filters = array() ): int {
     758            $wpdb    = self::get_connection();
     759            $table   = self::get_table_name( $wpdb );
     760            $blog_id = \get_current_blog_id();
     761
     762            $where    = array( 'blog_id = %d' );
     763            $bindings = array( $blog_id );
     764
     765            // Search filter.
     766            if ( ! empty( $filters['search'] ) ) {
     767                $search = '%' . $wpdb->esc_like( $filters['search'] ) . '%';
     768                $where[] = '(name LIKE %s OR tags LIKE %s)';
     769                $bindings[] = $search;
     770                $bindings[] = $search;
     771            }
     772
     773            // Status filter.
     774            if ( isset( $filters['status'] ) ) {
     775                $status_map = array(
     776                    'enabled'  => self::STATUS_ENABLED,
     777                    'disabled' => self::STATUS_DISABLED,
     778                    'trash'    => self::STATUS_TRASHED,
     779                );
     780
     781                if ( isset( $status_map[ $filters['status'] ] ) ) {
     782                    $where[] = 'status = %d';
     783                    $bindings[] = $status_map[ $filters['status'] ];
     784                } else {
     785                    $where[] = 'status >= %d';
     786                    $bindings[] = self::STATUS_DISABLED;
     787                }
     788            }
     789
     790            // Type filter.
     791            if ( ! empty( $filters['type'] ) && in_array( $filters['type'], array_keys( self::get_supported_types() ), true ) ) {
     792                $where[] = 'type = %s';
     793                $bindings[] = $filters['type'];
     794            }
     795
     796            $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';
     797            $query = 'SELECT COUNT(*) FROM ' . $table . ' ' . $where_sql;
     798
     799            $wpdb->suppress_errors( true );
     800            $count = (int) $wpdb->get_var( $wpdb->prepare( $query, $bindings ) );
     801            if ( '' !== $wpdb->last_error ) {
     802                if ( 1146 === self::get_last_sql_error( $wpdb ) ) {
     803                    if ( self::create_table( $wpdb ) ) {
     804                        $count = 0;
     805                    }
     806                }
     807            }
     808            $wpdb->suppress_errors( false );
     809
     810            return $count;
     811        }
    642812    }
    643813}
  • 0-day-analytics/trunk/classes/vendor/entities/class-wp-fatals-entity.php

    r3393178 r3451291  
    651651
    652652        /**
     653         * Alters the table for version 4.7.0 - adds performance indexes
     654         *
     655         * @return bool
     656         *
     657         * @since 4.7.0
     658         */
     659        public static function alter_table_470(): bool {
     660            $table_name = self::get_table_name();
     661            $indexes    = array(
     662                'severity'    => 'ADD KEY severity (severity)',
     663                'source_slug' => 'ADD KEY source_slug (source_slug)',
     664                'source_type' => 'ADD KEY source_type (source_type)',
     665            );
     666
     667            foreach ( $indexes as $index_sql ) {
     668                $sql = "ALTER TABLE `$table_name` $index_sql";
     669                // Use try-catch or check if index exists by attempting the alter
     670                // Since MySQL ignores duplicate key errors, we can safely run this.
     671                self::get_connection()->query( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- DDL operation, no caching needed.
     672            }
     673
     674            return true;
     675        }
     676
     677        /**
     678         * Prunes old fatal error records based on retention settings
     679         *
     680         * @param int $retention_days - Number of days to keep records (default 90)
     681         *
     682         * @return int Number of records deleted
     683         *
     684         * @since 4.7.0
     685         */
     686        public static function prune_old_records( int $retention_days = 90 ): int {
     687            global $wpdb;
     688            $cutoff_time = time() - ( $retention_days * 24 * 60 * 60 );
     689            $table_name  = self::get_table_name();
     690
     691            $sql = $wpdb->prepare(
     692                "DELETE FROM `$table_name` WHERE datetime < %d",
     693                $cutoff_time
     694            );
     695
     696            $result = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk delete operation.
     697
     698            return (int) $result;
     699        }
     700
     701        /**
    653702         * Generates drop down with all the subsites that have mail logs.
    654703         *
  • 0-day-analytics/trunk/classes/vendor/helpers/class-ajax-helper.php

    r3442115 r3451291  
    153153                \add_action( 'wp_ajax_aadvana_export_large_csv', array( __CLASS__, 'export_large_csv' ) );
    154154                \add_action( 'wp_ajax_aadvana_export_large_csv_cleanup', array( __CLASS__, 'export_large_csv_cleanup' ) );
     155                \add_action( 'wp_ajax_aadvana_get_table_info', array( __CLASS__, 'get_table_info' ) );
    155156
    156157                if ( Settings::get_option( 'server_info_module_enabled' ) ) {
     
    975976                    $snippet_type   = isset( $_POST['snippet_type'] ) ? \sanitize_text_field( wp_unslash( $_POST['snippet_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
    976977
    977                     $extra_file_name = '_snippets_';
    978 
    979                     $wpdb  = \ADVAN\Entities\Snippet_Entity::get_connection();
    980                     $table = \ADVAN\Entities\Snippet_Entity::get_table_name( $wpdb );
    981 
    982                     $where    = array( 'blog_id = %d' );
    983                     $bindings = array( get_current_blog_id() );
     978                    $filters = array();
    984979
    985980                    if ( '' !== $search ) {
    986                         $like       = '%' . $wpdb->esc_like( $search ) . '%';
    987                         $where[]    = '( name LIKE %s OR tags LIKE %s )';
    988                         $bindings[] = $like;
    989                         $bindings[] = $like;
     981                        $filters['search'] = $search;
    990982                    }
    991983
    992                     $status_map = array(
    993                         'enabled'  => \ADVAN\Entities\Snippet_Entity::STATUS_ENABLED,
    994                         'disabled' => \ADVAN\Entities\Snippet_Entity::STATUS_DISABLED,
    995                         'trash'    => \ADVAN\Entities\Snippet_Entity::STATUS_TRASHED,
    996                     );
    997 
    998                     if ( isset( $status_map[ $snippet_status ] ) ) {
    999                         $where[]    = 'status = %d';
    1000                         $bindings[] = $status_map[ $snippet_status ];
    1001                     } else {
    1002                         $where[]    = 'status >= %d';
    1003                         $bindings[] = \ADVAN\Entities\Snippet_Entity::STATUS_DISABLED;
     984                    if ( '' !== $snippet_status ) {
     985                        $filters['status'] = $snippet_status;
    1004986                    }
    1005987
    1006                     $types = array_keys( \ADVAN\Entities\Snippet_Entity::get_supported_types() );
    1007                     if ( '' !== $snippet_type && in_array( $snippet_type, $types, true ) ) {
    1008                         $where[]    = 'type = %s';
    1009                         $bindings[] = $snippet_type;
     988                    if ( '' !== $snippet_type ) {
     989                        $filters['type'] = $snippet_type;
    1010990                    }
    1011991
    1012                     $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';
    1013 
    1014                     $count_sql = 'SELECT COUNT(*) FROM ' . $table . ' ' . $where_sql;
    1015                     $total     = (int) $wpdb->get_var( $wpdb->prepare( $count_sql, $bindings ) );
    1016 
    1017                     $orderby = 'updated_at';
    1018                     $order   = 'DESC';
    1019 
    1020                     $list_sql   = 'SELECT * FROM ' . $table . ' ' . $where_sql . ' ORDER BY ' . $orderby . ' ' . $order . ' LIMIT %d OFFSET %d';
    1021                     $list_items = $wpdb->get_results(
    1022                         $wpdb->prepare(
    1023                             $list_sql,
    1024                             array_merge( $bindings, array( (int) $batch_size, (int) $offset ) )
    1025                         ),
    1026                         ARRAY_A
    1027                     );
    1028 
    1029                     $rows = $list_items ?: array();
     992                    $total = \ADVAN\Entities\Snippet_Entity::get_snippets_count_with_filters( $filters );
     993                    $rows  = \ADVAN\Entities\Snippet_Entity::get_snippets_with_filters( $filters, $offset, $batch_size );
    1030994                }
    1031995            } else {
     
    11761140         */
    11771141        public static function save_hook_group() {
    1178             if ( ! \current_user_can( 'manage_options' ) ) {
    1179                 \wp_send_json_error( 'Unauthorized' );
    1180             }
    1181 
    1182             \check_ajax_referer( 'advan_hook_groups', 'nonce' );
     1142            WP_Helper::verify_admin_nonce( 'advan_hook_groups', 'nonce' );
    11831143
    11841144            if ( ! class_exists( 'ADVAN\Entities\Hook_Groups_Entity' ) ) {
     
    12181178         */
    12191179        public static function delete_hook_group() {
    1220             if ( ! \current_user_can( 'manage_options' ) ) {
    1221                 \wp_send_json_error( 'Unauthorized' );
    1222             }
    1223 
    1224             \check_ajax_referer( 'advan_hook_groups', 'nonce' );
     1180            WP_Helper::verify_admin_nonce( 'advan_hook_groups', 'nonce' );
    12251181
    12261182            if ( ! class_exists( 'ADVAN\Entities\Hook_Groups_Entity' ) ) {
     
    12421198            }
    12431199        }
     1200
     1201        /**
     1202         * Get detailed table information including structure, indexes, foreign keys, and health.
     1203         *
     1204         * @return void
     1205         *
     1206         * @since 4.7.0
     1207         */
     1208        public static function get_table_info() {
     1209            WP_Helper::verify_admin_nonce( 'get_table_info', 'security' );
     1210
     1211            $table_name = isset( $_POST['table_name'] ) ? \sanitize_text_field( \wp_unslash( $_POST['table_name'] ) ) : '';
     1212
     1213            if ( empty( $table_name ) ) {
     1214                \wp_send_json_error( 'Invalid table name' );
     1215            }
     1216
     1217            $table_info = Common_Table::get_table_info( $table_name );
     1218
     1219            if ( \is_wp_error( $table_info ) ) {
     1220                \wp_send_json_error( $table_info->get_error_message() );
     1221            }
     1222
     1223            \wp_send_json_success( $table_info );
     1224        }
    12441225    }
    12451226}
  • 0-day-analytics/trunk/classes/vendor/helpers/class-hook-parameter-renderer.php

    r3442473 r3451291  
    1313namespace ADVAN\Helpers;
    1414
     15use ADVAN\Entities\Hooks_Management_Entity;
     16
    1517// Exit if accessed directly.
    1618if ( ! defined( 'ABSPATH' ) ) {
     
    3739         */
    3840        public static function render_parameters( string $hook_name, string $parameters ): string {
    39             global $wpdb;
    40 
    4141            // Get hook definition from hooks_management table.
    42             $management_table = $wpdb->prefix . ADVAN_PREFIX . 'hooks_management';
    43 
    44             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    45             $hook_config = $wpdb->get_row(
    46                 $wpdb->prepare(
    47                     "SELECT hook_parameters FROM `{$management_table}` WHERE hook_name = %s LIMIT 1",
    48                     $hook_name
    49                 ),
    50                 ARRAY_A
    51             );
     42            $hook_parameters = Hooks_Management_Entity::get_hook_parameters( $hook_name );
    5243
    5344            // Decode captured parameters.
     
    5950            // Get parameter definitions.
    6051            $param_defs = array();
    61             if ( $hook_config && ! empty( $hook_config['hook_parameters'] ) ) {
    62                 $param_defs = json_decode( $hook_config['hook_parameters'], true );
     52            if ( ! empty( $hook_parameters ) ) {
     53                $param_defs = json_decode( $hook_parameters, true );
    6354                if ( ! is_array( $param_defs ) ) {
    6455                    $param_defs = array();
     
    146137                    }
    147138                }
     139            }
     140
     141            if ( \is_array( $value ) && ! \in_array( $type, array( 'array', 'object' ), true ) ) {
     142                $type = 'array';
    148143            }
    149144            // Override type if actual value type differs significantly.
     
    216211         *
    217212         * @throws \Throwable If code execution fails.
     213         * @throws \Exception If unsafe code is detected.
    218214         *
    219215         * @since 4.5.0
     
    564560            $bool_val = filter_var( $value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
    565561            if ( is_null( $bool_val ) ) {
    566                 return '<code>' . \esc_html( $value ) . '</code>';
     562                return '<code>' . \esc_html( (string) $value ) . '</code>';
    567563            }
    568564            return $bool_val ? '<span style="color: green;">✓ true</span>' : '<span style="color: #999;">✗ false</span>';
     
    586582                return '<code>' . \esc_html( $str ) . '</code>';
    587583            }
    588             return '<code>' . \esc_html( print_r( $value, true ) ) . '</code>';
     584            return '<code>' . \esc_html( print_r( (array) $value, true ) ) . '</code>';
    589585        }
    590586
  • 0-day-analytics/trunk/classes/vendor/lists/class-fatals-list.php

    r3442115 r3451291  
    8181
    8282        /**
     83         * Maximum stack trace lines to display in details
     84         *
     85         * @var int
     86         *
     87         * @since 4.7.0
     88         */
     89        protected const MAX_STACK_TRACE_LINES = 50;
     90
     91        /**
    8392         * Holds the prepared options for speeding the process
    8493         *
     
    222231                    $plugin = -1;
    223232                } else {
    224                     $plugin = \sanitize_text_field( \wp_unslash( $_REQUEST['plugin'] ) );
     233                    $plugin_raw = \sanitize_text_field( \wp_unslash( $_REQUEST['plugin'] ) );
     234                    // Validate plugin slug format (alphanumeric, dashes, underscores, max 255 chars).
     235                    if ( preg_match( '/^[a-zA-Z0-9\-_]{1,255}$/', $plugin_raw ) ) {
     236                        $plugin = $plugin_raw;
     237                    } else {
     238                        $plugin = '';
     239                    }
    225240                }
    226241            } else {
     
    469484                            'action'   => 'log_source_view',
    470485                        );
     486                        $line_count = 0;
    471487                        foreach ( $reversed_details as $val ) {
     488                            if ( $line_count >= self::MAX_STACK_TRACE_LINES ) {
     489                                $message .= '<br>... (' . ( count( $reversed_details ) - $line_count ) . ' more lines truncated)';
     490                                break;
     491                            }
    472492
    473493                            $source_link = '';
     
    503523
    504524                            $message = \rtrim( $message, ' - ' );
     525                            $line_count++;
    505526                        }
    506527                        $message .= '</pre></div>';
  • 0-day-analytics/trunk/classes/vendor/lists/class-hooks-capture-list.php

    r3448917 r3451291  
    746746                    $hook_name  = '<code>' . \esc_html( $item[ $column_name ] ) . '</code>';
    747747
    748                     // Make hook name a link to hooks management if hooks_management_id is available
     748                    // Make hook name a link to hooks management if hooks_management_id is available.
    749749                    if ( ! empty( $item['hooks_management_id'] ) ) {
    750750                        $edit_url  = \network_admin_url( 'admin.php?page=advan_hooks_management&action=edit&id=' . absint( $item['hooks_management_id'] ) );
  • 0-day-analytics/trunk/classes/vendor/lists/class-hooks-management-list.php

    r3442115 r3451291  
    2222use ADVAN\Entities_Global\Common_Table;
    2323use ADVAN\Entities\Hooks_Management_Entity;
     24use ADVAN\Entities\Hook_Groups_Entity;
    2425use ADVAN\Lists\Views\Hooks_Management_View;
    2526
     
    452453         */
    453454        public function get_bulk_actions() {
    454             return array(
     455            $actions = array(
    455456                'delete'  => \esc_html__( 'Delete', '0-day-analytics' ),
    456457                'enable'  => \esc_html__( 'Enable', '0-day-analytics' ),
    457458                'disable' => \esc_html__( 'Disable', '0-day-analytics' ),
    458459            );
     460
     461            // Add group assignment actions.
     462            $groups = Hook_Groups_Entity::get_groups_array();
     463            if ( ! empty( $groups ) ) {
     464                foreach ( $groups as $group_id => $group ) {
     465                    $actions[ 'assign_group_' . $group_id ] = sprintf(
     466                        /* translators: %s: Group name */
     467                        \esc_html__( 'Assign to group: %s', '0-day-analytics' ),
     468                        $group['name']
     469                    );
     470                }
     471            }
     472
     473            return $actions;
    459474        }
    460475
     
    539554
    540555            // Handle bulk actions.
    541             if ( in_array( $action, array( 'delete', 'enable', 'disable' ), true ) ) {
     556            if ( in_array( $action, array( 'delete', 'enable', 'disable' ), true ) || ( is_string( $action ) && strpos( $action, 'assign_group_' ) === 0 ) ) {
    542557                $nonce = isset( $_REQUEST['_wpnonce'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['_wpnonce'] ) ) : '';
    543558
     
    550565
    551566                if ( ! empty( $ids ) ) {
    552                     global $wpdb;
    553                     $table = Hooks_Management_Entity::get_table_name();
    554 
    555567                    foreach ( $ids as $id ) {
    556568                        if ( 'delete' === $action ) {
    557569                            Hooks_Management_Entity::delete_by_id( $id );
    558570                        } elseif ( 'enable' === $action ) {
    559                             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    560                             $wpdb->query(
    561                                 $wpdb->prepare(
    562                                     "UPDATE `{$table}` SET enabled = 1, date_modified = %f WHERE id = %d",
    563                                     microtime( true ),
    564                                     $id
    565                                 )
    566                             );
     571                            Hooks_Management_Entity::set_enabled( $id, true );
    567572                        } elseif ( 'disable' === $action ) {
    568                             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    569                             $wpdb->query(
    570                                 $wpdb->prepare(
    571                                     "UPDATE `{$table}` SET enabled = 0, date_modified = %f WHERE id = %d",
    572                                     microtime( true ),
    573                                     $id
    574                                 )
    575                             );
     573                            Hooks_Management_Entity::set_enabled( $id, false );
     574                        } elseif ( is_string( $action ) && strpos( $action, 'assign_group_' ) === 0 ) {
     575                            // Extract group ID from action.
     576                            $group_id = str_replace( 'assign_group_', '', $action );
     577                            $group_id = absint( $group_id );
     578
     579                            // Update the group_id for this hook.
     580                            Hooks_Management_Entity::set_group_id( $id, $group_id );
    576581                        }
    577582                    }
  • 0-day-analytics/trunk/classes/vendor/lists/class-logs-list.php

    r3442115 r3451291  
    365365                    $result = Reverse_Line_Reader::read_file_from_end(
    366366                        $file,
    367                         function( $line, $pos ) use ( &$collected_items, &$errors, &$position ) {
     367                        function( $line, $pos ) use ( &$collected_items, &$errors, &$position, $items, $write_temp ) {
    368368
    369369                            $position = $pos;
     370
     371                            // Prevent memory exhaustion from extremely large multi-line errors.
     372                            if ( count( $collected_items ) > 500 ) { // Reasonable limit for stack traces.
     373                                $collected_items = array_slice( $collected_items, -100 ); // Keep last 100 items.
     374                                // if ( function_exists( 'error_log' ) ) {
     375                                //  error_log( 'Logs_List: Truncated collected_items due to size limit (' . count( $collected_items ) . ' items)' );
     376                                // }
     377                            }
    370378
    371379                            // Flag that holds the status of the error - are there more lines to read or not.
     
    387395                                    $errors[]      = $parsed_data;
    388396                                    $more_to_error = false;
     397
     398                                    // Prevent memory exhaustion from too many errors.
     399                                    if ( count( $errors ) >= $items * 2 ) { // Allow some buffer.
     400                                        return array(
     401                                            'close'     => true,
     402                                            'line_done' => true,
     403                                            'no_flush'  => false,
     404                                        );
     405                                    }
    389406                                } elseif ( \is_array( $parsed_data ) ) {
    390407                                    if ( isset( $parsed_data['call'] ) && str_starts_with( trim( $parsed_data['call'] ), 'made by' ) ) {
     
    12331250                            }
    12341251                            ?>
    1235                                 <option <?php echo ( $selected ); ?> value="<?php echo \esc_attr( $name ); ?>"><?php echo \esc_attr( $name );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></option>
     1252                                <option <?php echo ( $selected ); ?> value="<?php echo \esc_attr( $name ); ?>"><?php echo \esc_html( $name ); ?></option>
    12361253                                <?php
    12371254                        }
  • 0-day-analytics/trunk/classes/vendor/lists/class-table-list.php

    r3442115 r3451291  
    643643                   
    644644                </select>
     645                <?php if ( 'top' === $which ) : ?>
     646                <button id="table-info-btn" class="button button-secondary" title="<?php esc_attr_e( 'View table information', '0-day-analytics' ); ?>">
     647                    <span class="dashicons dashicons-info"></span>
     648                    <?php esc_html_e( 'Info', '0-day-analytics' ); ?>
     649                </button>
     650                <?php endif; ?>
    645651               
    646652            </div>
     
    667673                <?php
    668674            }
     675
     676            // Table info modal - only for top navigation
     677            if ( 'top' === $which ) :
     678            ?>
     679            <div id="table-info-modal" class="table-info-modal" style="display: none;">
     680                <div class="table-info-modal-content">
     681                    <div class="table-info-modal-header">
     682                        <h3><?php printf( esc_html__( 'Table Information: %s', '0-day-analytics' ), esc_html( self::$table::get_name() ) ); ?></h3>
     683                        <button type="button" class="table-info-modal-close">&times;</button>
     684                    </div>
     685                    <div class="table-info-modal-body">
     686                        <div class="table-info-loading">
     687                            <span class="spinner is-active"></span>
     688                            <?php esc_html_e( 'Loading table information...', '0-day-analytics' ); ?>
     689                        </div>
     690                        <div class="table-info-content" style="display: none;">
     691                            <div class="table-info-section">
     692                                <h4><?php esc_html_e( 'Table Structure', '0-day-analytics' ); ?></h4>
     693                                <div class="table-structure-info"></div>
     694                            </div>
     695                            <div class="table-info-section">
     696                                <h4><?php esc_html_e( 'Indexes', '0-day-analytics' ); ?></h4>
     697                                <div class="table-indexes-info"></div>
     698                            </div>
     699                            <div class="table-info-section">
     700                                <h4><?php esc_html_e( 'Foreign Keys', '0-day-analytics' ); ?></h4>
     701                                <div class="table-foreign-keys-info"></div>
     702                            </div>
     703                            <div class="table-info-section">
     704                                <h4><?php esc_html_e( 'Table Health', '0-day-analytics' ); ?></h4>
     705                                <div class="table-health-info"></div>
     706                            </div>
     707                        </div>
     708                    </div>
     709                </div>
     710            </div>
     711            <?php endif; ?>
     712
     713            <?php
    669714            // if ( 'top' === $which ) {
    670715            global $wpdb;
     
    836881                makeSearchableDropdown(document.getElementById("table_filter_<?php echo \esc_attr( $which ); ?>"));
    837882
     883                <?php if ( 'top' === $which ) { ?>
     884                // Table info modal functionality
     885                const tableInfoBtn = document.getElementById('table-info-btn');
     886                const tableInfoModal = document.getElementById('table-info-modal');
     887                const tableInfoClose = document.querySelector('.table-info-modal-close');
     888
     889                if (tableInfoBtn && tableInfoModal) {
     890                    tableInfoBtn.addEventListener('click', function(e) {
     891                        e.preventDefault();
     892                        loadTableInfo();
     893                        tableInfoModal.style.display = 'block';
     894                    });
     895
     896                    tableInfoClose.addEventListener('click', function() {
     897                        tableInfoModal.style.display = 'none';
     898                    });
     899
     900                    window.addEventListener('click', function(e) {
     901                        if (e.target === tableInfoModal) {
     902                            tableInfoModal.style.display = 'none';
     903                        }
     904                    });
     905                }
     906
     907                function loadTableInfo() {
     908                    const loadingEl = document.querySelector('.table-info-loading');
     909                    const contentEl = document.querySelector('.table-info-content');
     910
     911                    loadingEl.style.display = 'block';
     912                    contentEl.style.display = 'none';
     913
     914                    fetch('<?php echo esc_url(admin_url('admin-ajax.php')); ?>', {
     915                        method: 'POST',
     916                        headers: {
     917                            'Content-Type': 'application/x-www-form-urlencoded',
     918                        },
     919                        body: new URLSearchParams({
     920                            action: 'aadvana_get_table_info',
     921                            table_name: '<?php echo esc_js(self::$table::get_name()); ?>',
     922                            security: '<?php echo wp_create_nonce('get_table_info'); ?>'
     923                        })
     924                    })
     925                    .then(response => response.json())
     926                    .then(data => {
     927                        loadingEl.style.display = 'none';
     928                        if (data.success) {
     929                            displayTableInfo(data.data);
     930                            contentEl.style.display = 'block';
     931                        } else {
     932                            alert('<?php esc_html_e('Error loading table information', '0-day-analytics'); ?>: ' + data.data);
     933                        }
     934                    })
     935                    .catch(error => {
     936                        loadingEl.style.display = 'none';
     937                        alert('<?php esc_html_e('Error loading table information', '0-day-analytics'); ?>: ' + error.message);
     938                    });
     939                }
     940
     941                function displayTableInfo(info) {
     942                    // Table Structure
     943                    const structureEl = document.querySelector('.table-structure-info');
     944                    if (info.structure && info.structure.length > 0) {
     945                        let html = '<table class="widefat striped"><thead><tr><th><?php esc_html_e('Column', '0-day-analytics'); ?></th><th><?php esc_html_e('Type', '0-day-analytics'); ?></th><th><?php esc_html_e('Null', '0-day-analytics'); ?></th><th><?php esc_html_e('Key', '0-day-analytics'); ?></th><th><?php esc_html_e('Default', '0-day-analytics'); ?></th><th><?php esc_html_e('Extra', '0-day-analytics'); ?></th></tr></thead><tbody>';
     946                        info.structure.forEach(col => {
     947                            html += `<tr>
     948                                <td><code>${escapeHtml(col.Field)}</code></td>
     949                                <td><code>${escapeHtml(col.Type)}</code></td>
     950                                <td>${escapeHtml(col.Null)}</td>
     951                                <td>${escapeHtml(col.Key || '')}</td>
     952                                <td>${escapeHtml(col.Default || '')}</td>
     953                                <td>${escapeHtml(col.Extra || '')}</td>
     954                            </tr>`;
     955                        });
     956                        html += '</tbody></table>';
     957                        structureEl.innerHTML = html;
     958                    } else {
     959                        structureEl.innerHTML = '<p><?php esc_html_e('No column information available', '0-day-analytics'); ?></p>';
     960                    }
     961
     962                    // Indexes
     963                    const indexesEl = document.querySelector('.table-indexes-info');
     964                    if (info.indexes && info.indexes.length > 0) {
     965                        let html = '<table class="widefat striped"><thead><tr><th><?php esc_html_e('Key Name', '0-day-analytics'); ?></th><th><?php esc_html_e('Column', '0-day-analytics'); ?></th><th><?php esc_html_e('Unique', '0-day-analytics'); ?></th><th><?php esc_html_e('Type', '0-day-analytics'); ?></th></tr></thead><tbody>';
     966                        info.indexes.forEach(idx => {
     967                            html += `<tr>
     968                                <td><code>${escapeHtml(idx.Key_name)}</code></td>
     969                                <td><code>${escapeHtml(idx.Column_name)}</code></td>
     970                                <td>${idx.Non_unique === '0' ? '<?php esc_html_e('Yes', '0-day-analytics'); ?>' : '<?php esc_html_e('No', '0-day-analytics'); ?>'}</td>
     971                                <td>${escapeHtml(idx.Index_type)}</td>
     972                            </tr>`;
     973                        });
     974                        html += '</tbody></table>';
     975                        indexesEl.innerHTML = html;
     976                    } else {
     977                        indexesEl.innerHTML = '<p><?php esc_html_e('No indexes found', '0-day-analytics'); ?></p>';
     978                    }
     979
     980                    // Foreign Keys
     981                    const fkEl = document.querySelector('.table-foreign-keys-info');
     982                    if (info.foreign_keys && info.foreign_keys.length > 0) {
     983                        let html = '<table class="widefat striped"><thead><tr><th><?php esc_html_e('Constraint', '0-day-analytics'); ?></th><th><?php esc_html_e('Column', '0-day-analytics'); ?></th><th><?php esc_html_e('Referenced Table', '0-day-analytics'); ?></th><th><?php esc_html_e('Referenced Column', '0-day-analytics'); ?></th></tr></thead><tbody>';
     984                        info.foreign_keys.forEach(fk => {
     985                            html += `<tr>
     986                                <td><code>${escapeHtml(fk.constraint_name)}</code></td>
     987                                <td><code>${escapeHtml(fk.column_name)}</code></td>
     988                                <td><code>${escapeHtml(fk.referenced_table_name)}</code></td>
     989                                <td><code>${escapeHtml(fk.referenced_column_name)}</code></td>
     990                            </tr>`;
     991                        });
     992                        html += '</tbody></table>';
     993                        fkEl.innerHTML = html;
     994                    } else {
     995                        fkEl.innerHTML = '<p><?php esc_html_e('No foreign keys found', '0-day-analytics'); ?></p>';
     996                    }
     997
     998                    // Table Health
     999                    const healthEl = document.querySelector('.table-health-info');
     1000                    if (info.health) {
     1001                        let html = '<ul>';
     1002                        if (info.health.needs_optimization) {
     1003                            html += `<li><span style="color: #d63638;">⚠️ <?php esc_html_e('Table may benefit from optimization', '0-day-analytics'); ?></span></li>`;
     1004                        } else {
     1005                            html += `<li><span style="color: #00a32a;">✅ <?php esc_html_e('Table appears to be well optimized', '0-day-analytics'); ?></span></li>`;
     1006                        }
     1007                        if (info.health.row_count) {
     1008                            html += `<li><?php esc_html_e('Row count', '0-day-analytics'); ?>: ${info.health.row_count.toLocaleString()}</li>`;
     1009                        }
     1010                        if (info.health.data_size) {
     1011                            html += `<li><?php esc_html_e('Data size', '0-day-analytics'); ?>: ${formatBytes(info.health.data_size)}</li>`;
     1012                        }
     1013                        if (info.health.index_size) {
     1014                            html += `<li><?php esc_html_e('Index size', '0-day-analytics'); ?>: ${formatBytes(info.health.index_size)}</li>`;
     1015                        }
     1016                        html += '</ul>';
     1017                        healthEl.innerHTML = html;
     1018                    } else {
     1019                        healthEl.innerHTML = '<p><?php esc_html_e('Health information not available', '0-day-analytics'); ?></p>';
     1020                    }
     1021                }
     1022
     1023                function escapeHtml(text) {
     1024                    const div = document.createElement('div');
     1025                    div.textContent = text;
     1026                    return div.innerHTML;
     1027                }
     1028
     1029                function formatBytes(bytes) {
     1030                    if (bytes === 0) return '0 Bytes';
     1031                    const k = 1024;
     1032                    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
     1033                    const i = Math.floor(Math.log(bytes) / Math.log(k));
     1034                    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
     1035                }
     1036                <?php } ?>
     1037
    8381038            </script>
    8391039                        <?php
     
    9401140                        top: -9999px;
    9411141                        }
     1142
     1143                        /* Table Info Modal Styles */
     1144                        .table-info-modal {
     1145                        position: fixed;
     1146                        z-index: 100000;
     1147                        left: 0;
     1148                        top: 0;
     1149                        width: 100%;
     1150                        height: 100%;
     1151                        background-color: rgba(0, 0, 0, 0.5);
     1152                        }
     1153
     1154                        .table-info-modal-content {
     1155                        background-color: #fefefe;
     1156                        margin: 5% auto;
     1157                        padding: 0;
     1158                        border: 1px solid #888;
     1159                        width: 90%;
     1160                        max-width: 1000px;
     1161                        max-height: 80vh;
     1162                        overflow-y: auto;
     1163                        border-radius: 8px;
     1164                        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
     1165                        }
     1166
     1167                        .aadvana-darkskin .table-info-modal-content {
     1168                        background-color: #1d456b !important;
     1169                        border-color: #555;
     1170                        }
     1171
     1172                        .table-info-modal-header {
     1173                        padding: 15px 20px;
     1174                        /* background: #f8f9fa; */
     1175                        border-bottom: 1px solid #dee2e6;
     1176                        display: flex;
     1177                        justify-content: space-between;
     1178                        align-items: center;
     1179                        border-radius: 8px 8px 0 0;
     1180                        }
     1181
     1182                        .table-info-modal-header h3 {
     1183                        margin: 0;
     1184                        /* color: #333; */
     1185                        font-size: 18px;
     1186                        font-weight: 600;
     1187                        }
     1188
     1189                        .table-info-modal-close {
     1190                        background: none;
     1191                        border: none;
     1192                        font-size: 24px;
     1193                        font-weight: bold;
     1194                        /* color: #666; */
     1195                        cursor: pointer;
     1196                        padding: 0;
     1197                        width: 30px;
     1198                        height: 30px;
     1199                        display: flex;
     1200                        align-items: center;
     1201                        justify-content: center;
     1202                        border-radius: 50%;
     1203                        transition: background-color 0.2s;
     1204                        }
     1205
     1206                        .table-info-modal-close:hover {
     1207                        /* background-color: #e9ecef; */
     1208                        /* color: #333; */
     1209                        }
     1210
     1211                        .table-info-modal-body {
     1212                        padding: 20px;
     1213                        }
     1214
     1215                        .table-info-loading {
     1216                        text-align: center;
     1217                        padding: 40px;
     1218                        }
     1219
     1220                        .table-info-loading .spinner {
     1221                        float: none;
     1222                        margin: 0 auto 10px;
     1223                        }
     1224
     1225                        .table-info-section {
     1226                        margin-bottom: 30px;
     1227                        }
     1228
     1229                        .table-info-section h4 {
     1230                        margin: 0 0 15px 0;
     1231                        padding: 10px 15px;
     1232                        /* background: #f8f9fa; */
     1233                        border-left: 4px solid #007cba;
     1234                        font-size: 16px;
     1235                        font-weight: 600;
     1236                        /* color: #333; */
     1237                        }
     1238
     1239                        .table-info-section table {
     1240                        margin: 0;
     1241                        }
     1242
     1243                        .table-info-section table code {
     1244                        /* background: #f8f9fa; */
     1245                        padding: 2px 6px;
     1246                        border-radius: 3px;
     1247                        font-size: 12px;
     1248                        /* color: #495057; */
     1249                        }
     1250
     1251                        .table-info-section ul {
     1252                        margin: 0;
     1253                        padding-left: 20px;
     1254                        }
     1255
     1256                        .table-info-section li {
     1257                        margin-bottom: 8px;
     1258                        }
     1259
     1260                        @media (max-width: 768px) {
     1261                        .table-info-modal-content {
     1262                            margin: 2% auto;
     1263                            width: 95%;
     1264                            max-height: 90vh;
     1265                        }
     1266
     1267                        .table-info-modal-body {
     1268                            padding: 15px;
     1269                        }
     1270
     1271                        .table-info-section table {
     1272                            font-size: 12px;
     1273                        }
     1274
     1275                        .table-info-section table th,
     1276                        .table-info-section table td {
     1277                            padding: 8px 4px;
     1278                        }
     1279                        }
    9421280                    </style>
    9431281                        <?php } ?>
  • 0-day-analytics/trunk/classes/vendor/lists/entity/class-common-table.php

    r3442115 r3451291  
    20812081            }
    20822082        }
     2083
     2084        /**
     2085         * Get detailed table information including structure, indexes, foreign keys, and health.
     2086         *
     2087         * @param string $table_name The table name to get information for.
     2088         * @param \wpdb  $connection Optional database connection.
     2089         *
     2090         * @return array|\WP_Error Array of table information or WP_Error on failure.
     2091         *
     2092         * @since 4.7.0
     2093         */
     2094        public static function get_table_info( string $table_name, $connection = null ) {
     2095            if ( null !== $connection ) {
     2096                if ( $connection instanceof \wpdb ) {
     2097                    $_wpdb = $connection;
     2098                } else {
     2099                    global $wpdb;
     2100                    $_wpdb = $wpdb;
     2101                }
     2102            } else {
     2103                global $wpdb;
     2104                $_wpdb = $wpdb;
     2105            }
     2106
     2107            // Validate table name.
     2108            if ( ! self::validate_table_name( $table_name ) ) {
     2109                return new \WP_Error( 'invalid_table', 'Invalid table name.' );
     2110            }
     2111
     2112            // Check if table exists.
     2113            if ( ! self::check_table_exists( $table_name, $_wpdb ) ) {
     2114                return new \WP_Error( 'table_not_found', 'Table does not exist.' );
     2115            }
     2116
     2117            $table_info = array();
     2118
     2119            try {
     2120                // Get table structure (columns) - using DESC command like other methods.
     2121                $table_info['structure'] = $_wpdb->get_results(
     2122                    $_wpdb->prepare( 'DESC %i', $table_name ),
     2123                    ARRAY_A
     2124                );
     2125
     2126                // Get indexes - using SHOW INDEX command.
     2127                $table_info['indexes'] = $_wpdb->get_results(
     2128                    $_wpdb->prepare( 'SHOW INDEX FROM %i', $table_name ),
     2129                    ARRAY_A
     2130                );
     2131
     2132                // Get foreign keys from information_schema - using prepared query.
     2133                $table_info['foreign_keys'] = $_wpdb->get_results(
     2134                    $_wpdb->prepare(
     2135                        'SELECT
     2136                            kcu.constraint_name,
     2137                            kcu.column_name,
     2138                            kcu.referenced_table_name,
     2139                            kcu.referenced_column_name
     2140                        FROM information_schema.key_column_usage kcu
     2141                        JOIN information_schema.table_constraints tc
     2142                            ON kcu.constraint_name = tc.constraint_name
     2143                            AND kcu.table_schema = tc.table_schema
     2144                        WHERE kcu.table_schema = %s
     2145                            AND kcu.table_name = %s
     2146                            AND tc.constraint_type = %s',
     2147                        $_wpdb->dbname,
     2148                        $table_name,
     2149                        'FOREIGN KEY'
     2150                    ),
     2151                    ARRAY_A
     2152                );
     2153
     2154                // Get table health information from information_schema - using prepared query.
     2155                $health_info = $_wpdb->get_row(
     2156                    $_wpdb->prepare(
     2157                        'SELECT
     2158                            table_rows as row_count,
     2159                            data_length as data_size,
     2160                            index_length as index_size,
     2161                            data_free as fragmentation
     2162                        FROM information_schema.tables
     2163                        WHERE table_schema = %s AND table_name = %s',
     2164                        $_wpdb->dbname,
     2165                        $table_name
     2166                    ),
     2167                    ARRAY_A
     2168                );
     2169
     2170                $table_info['health'] = array(
     2171                    'row_count' => $health_info ? (int) $health_info['row_count'] : 0,
     2172                    'data_size' => $health_info ? (int) $health_info['data_size'] : 0,
     2173                    'index_size' => $health_info ? (int) $health_info['index_size'] : 0,
     2174                    'needs_optimization' => $health_info && (int) $health_info['fragmentation'] > 0,
     2175                );
     2176
     2177            } catch ( \Exception $e ) {
     2178                return new \WP_Error( 'database_error', 'Database error: ' . $e->getMessage() );
     2179            }
     2180
     2181            return $table_info;
     2182        }
    20832183    }
    20842184}
  • 0-day-analytics/trunk/classes/vendor/lists/views/class-hooks-management-view.php

    r3442115 r3451291  
    172172
    173173            if ( $id ) {
    174                 global $wpdb;
    175                 $table = Hooks_Management_Entity::get_table_name();
    176 
    177                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    178                 $hook = $wpdb->get_row(
    179                     $wpdb->prepare( 'SELECT * FROM `' . $table . '` WHERE id = %d', $id ),
    180                     ARRAY_A
    181                 );
     174                $hook = Hooks_Management_Entity::load( 'id = %d', array( $id ) );
    182175
    183176                if ( ! $hook ) {
  • 0-day-analytics/trunk/classes/vendor/lists/views/class-logs-list-view.php

    r3391413 r3451291  
    3131     */
    3232    class Logs_List_View {
    33 
    3433        /**
    3534         * Displays the settings page.
     
    5958            <?php
    6059
    61             $plugin_filter = $_REQUEST['plugin_filter'] ?? ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     60            // Verify nonce for security
     61            if ( isset( $_REQUEST['advanced-analytics-security'] ) && \wp_verify_nonce( \sanitize_key( \wp_unslash( $_REQUEST['advanced-analytics-security'] ) ), 'advan-plugin-data' ) ) {
     62                $plugin_filter = isset( $_REQUEST['plugin_filter'] ) ? \sanitize_text_field( \wp_unslash( $_REQUEST['plugin_filter'] ) ) : '';
     63            } else {
     64                $plugin_filter = '';
     65            }
    6266
    63             $plugin_filter = \sanitize_text_field( \wp_unslash( $plugin_filter ) );
    64             $events_list   = new Logs_List(
     67            $events_list = new Logs_List(
    6568                array(
    6669                    'plugin_filter' => $plugin_filter,
     
    103106            }
    104107
    105             if ( ! empty( $_GET['single_severity_filter_top'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Verified via WP_Helper::verify_admin_nonce below.
     108            if ( ! empty( $_GET['single_severity_filter_top'] ) ) {
    106109                WP_Helper::verify_admin_nonce( 'advan-plugin-data', 'advanced-analytics-security' );
    107110
    108111                // Validate and strictly compare plugin filter against known plugin bases.
    109                 if ( isset( $_GET['plugin_filter'] ) && '' !== $_GET['plugin_filter'] && -1 !== (int) $_GET['plugin_filter'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above.
    110                     $raw_plugin_filter = \sanitize_text_field( \wp_unslash( (string) $_GET['plugin_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above.
     112                if ( isset( $_GET['plugin_filter'] ) && '' !== $_GET['plugin_filter'] && -1 !== (int) $_GET['plugin_filter'] ) {
     113                    $raw_plugin_filter = \sanitize_text_field( \wp_unslash( (string) $_GET['plugin_filter'] ) );
    111114                    if ( ! \in_array( $raw_plugin_filter, Plugin_Theme_Helper::get_plugins_bases(), true ) ) {
    112                         \wp_safe_redirect(
    113                             \remove_query_arg(
    114                                 array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action', 'plugin_filter' ),
    115                                 isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''
    116                             )
    117                         );
    118                         exit;
     115                        if ( ! \in_array( $raw_plugin_filter, Plugin_Theme_Helper::get_plugins_bases(), true ) ) {
     116                            \wp_safe_redirect(
     117                                \remove_query_arg(
     118                                    array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action', 'plugin_filter' ),
     119                                    isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''
     120                                )
     121                            );
     122                            exit;
     123                        }
    119124                    }
     125
     126                    \wp_safe_redirect(
     127                        \remove_query_arg( array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action' ), isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' )
     128                    );
     129                    exit;
    120130                }
    121 
    122                 \wp_safe_redirect(
    123                     \remove_query_arg( array( 'severity_filter', 'bulk_action', 'single_severity_filter_top', 'filter_action' ), isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' )
    124                 );
    125                 exit;
    126131            }
    127132        }
  • 0-day-analytics/trunk/readme.txt

    r3448917 r3451291  
    55Tested up to: 6.9
    66Requires PHP: 7.4
    7 Stable tag: 4.6.0
     7Stable tag: 4.7.0
    88License: GPLv3 or later
    99License URI: https://www.gnu.org/licenses/gpl-3.0.txt
     
    9393== Changelog ==
    9494
     95= 4.7.0 =
     96* Hooks module improvements - group assignment in bulk. Bug fixes and code optimizations.
     97
    9598= 4.6.0 =
    9699* Hooks module improvements and small styling issues fixed.
Note: See TracChangeset for help on using the changeset viewer.