verygoodplugins

Untitled

Sep 17th, 2025
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 10.74 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  * WP Fusion Integration Action Scraper
  5.  *
  6.  * Captures all action outputs from WP Fusion integrations and dumps them
  7.  * for LLM/AI analysis.
  8.  *
  9.  * @package   WP Fusion
  10.  * @copyright Copyright (c) 2024, Very Good Plugins, https://verygoodplugins.com
  11.  * @license   GPL-3.0+
  12.  * @since     x.x.x
  13.  */
  14.  
  15. if ( ! defined( 'ABSPATH' ) ) {
  16.     exit; // Exit if accessed directly
  17. }
  18.  
  19. class WPF_Integration_Scraper {
  20.  
  21.     /**
  22.      * List of integration classes to monitor.
  23.      *
  24.      * @since x.x.x
  25.      * @var array
  26.      */
  27.     private $monitored_integrations = array(
  28.         'WPF_BuddyPress',
  29.         'WPF_Woocommerce',
  30.         'WPF_GForms_Integration',
  31.         'WPF_LearnDash',
  32.         'WPF_Contact_Form_7',
  33.         'WPF_Ninja_Forms',
  34.         'WPF_WPForms',
  35.         'WPF_Easy_Digital_Downloads',
  36.         'WPF_LifterLMS',
  37.         'WPF_Ultimate_Member',
  38.         'WPF_Affiliate_WP',
  39.         'WPF_Wishlist_Member'
  40.     );
  41.  
  42.     /**
  43.      * Actions to capture from each integration.
  44.      *
  45.      * @since x.x.x
  46.      * @var array
  47.      */
  48.     private $target_actions = array();
  49.  
  50.     /**
  51.      * Captured data storage.
  52.      *
  53.      * @since x.x.x
  54.      * @var array
  55.      */
  56.     private $captured_data = array();
  57.  
  58.     /**
  59.      * File path for dumping captured data.
  60.      *
  61.      * @since x.x.x
  62.      * @var string
  63.      */
  64.     private $dump_file;
  65.  
  66.     /**
  67.      * Initialize the scraper.
  68.      *
  69.      * @since x.x.x
  70.      */
  71.     public function __construct() {
  72.         $this->dump_file = WP_CONTENT_DIR . '/wpf-integration-dump.json';
  73.         $this->init();
  74.     }
  75.  
  76.     /**
  77.      * Initialize scraper hooks.
  78.      *
  79.      * @since x.x.x
  80.      */
  81.     public function init() {
  82.         // Wait until all plugins are loaded.
  83.         add_action( 'plugins_loaded', array( $this, 'scrape_integration_actions' ), 999 );
  84.        
  85.         // Hook into WordPress shutdown to dump final data.
  86.         add_action( 'shutdown', array( $this, 'dump_captured_data' ) );
  87.     }
  88.  
  89.     /**
  90.      * Dynamically scrape all add_action calls from integration classes.
  91.      *
  92.      * @since x.x.x
  93.      */
  94.     public function scrape_integration_actions() {
  95.        
  96.         foreach ( $this->monitored_integrations as $class_name ) {
  97.             if ( ! class_exists( $class_name ) ) {
  98.                 continue;
  99.             }
  100.  
  101.             // Get reflection of the class.
  102.             $reflection = new ReflectionClass( $class_name );
  103.            
  104.             // Get the file contents.
  105.             $file_path = $reflection->getFileName();
  106.             if ( ! $file_path || ! file_exists( $file_path ) ) {
  107.                 continue;
  108.             }
  109.  
  110.             $file_contents = file_get_contents( $file_path );
  111.            
  112.             // Extract all add_action calls.
  113.             $this->extract_actions_from_content( $file_contents, $class_name );
  114.         }
  115.  
  116.         // Now hook into all discovered actions.
  117.         $this->hook_into_discovered_actions();
  118.     }
  119.  
  120.     /**
  121.      * Extract add_action calls from file content.
  122.      *
  123.      * @since x.x.x
  124.      *
  125.      * @param string $content    File content.
  126.      * @param string $class_name Class name.
  127.      */
  128.     private function extract_actions_from_content( $content, $class_name ) {
  129.        
  130.         // Regex to match add_action calls.
  131.         $pattern = '/add_action\s*\(\s*[\'"]([^\'"]+)[\'"]\s*,\s*array\s*\(\s*\$this\s*,\s*[\'"]([^\'"]+)[\'"]\s*\)/';
  132.        
  133.         preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER );
  134.        
  135.         foreach ( $matches as $match ) {
  136.             $action_name = $match[1];
  137.             $method_name = $match[2];
  138.            
  139.             if ( ! isset( $this->target_actions[ $class_name ] ) ) {
  140.                 $this->target_actions[ $class_name ] = array();
  141.             }
  142.            
  143.             $this->target_actions[ $class_name ][] = array(
  144.                 'action' => $action_name,
  145.                 'method' => $method_name,
  146.             );
  147.         }
  148.  
  149.         // Also match add_filter calls.
  150.         $filter_pattern = '/add_filter\s*\(\s*[\'"]([^\'"]+)[\'"]\s*,\s*array\s*\(\s*\$this\s*,\s*[\'"]([^\'"]+)[\'"]\s*\)/';
  151.        
  152.         preg_match_all( $filter_pattern, $content, $filter_matches, PREG_SET_ORDER );
  153.        
  154.         foreach ( $filter_matches as $match ) {
  155.             $filter_name = $match[1];
  156.             $method_name = $match[2];
  157.            
  158.             if ( ! isset( $this->target_actions[ $class_name ] ) ) {
  159.                 $this->target_actions[ $class_name ] = array();
  160.             }
  161.            
  162.             $this->target_actions[ $class_name ][] = array(
  163.                 'action' => $filter_name,
  164.                 'method' => $method_name,
  165.                 'type'   => 'filter',
  166.             );
  167.         }
  168.     }
  169.  
  170.     /**
  171.      * Hook into all discovered actions with our universal capture handler.
  172.      *
  173.      * @since x.x.x
  174.      */
  175.     private function hook_into_discovered_actions() {
  176.        
  177.         foreach ( $this->target_actions as $class_name => $actions ) {
  178.             foreach ( $actions as $action_data ) {
  179.                 $action_name = $action_data['action'];
  180.                 $method_name = $action_data['method'];
  181.                 $type        = isset( $action_data['type'] ) ? $action_data['type'] : 'action';
  182.                
  183.                 // Hook with very high priority to capture everything.
  184.                 if ( 'filter' === $type ) {
  185.                     add_filter( $action_name, function( $value ) use ( $class_name, $action_name, $method_name ) {
  186.                         return $this->universal_capture_handler( $value, $class_name, $action_name, $method_name, func_get_args(), 'filter' );
  187.                     }, 9999, 10 );
  188.                 } else {
  189.                     add_action( $action_name, function() use ( $class_name, $action_name, $method_name ) {
  190.                         $this->universal_capture_handler( null, $class_name, $action_name, $method_name, func_get_args(), 'action' );
  191.                     }, 9999, 10 );
  192.                 }
  193.             }
  194.         }
  195.     }
  196.  
  197.     /**
  198.      * Universal handler that captures all action/filter data.
  199.      *
  200.      * @since x.x.x
  201.      *
  202.      * @param mixed  $value       Filter value (null for actions).
  203.      * @param string $class_name  Integration class name.
  204.      * @param string $action_name Action/filter name.
  205.      * @param string $method_name Method name.
  206.      * @param array  $args        All arguments passed to the action/filter.
  207.      * @param string $type        'action' or 'filter'.
  208.      * @return mixed
  209.      */
  210.     private function universal_capture_handler( $value, $class_name, $action_name, $method_name, $args, $type = 'action' ) {
  211.        
  212.         // Capture the data.
  213.         $capture_entry = array(
  214.             'timestamp'   => current_time( 'mysql' ),
  215.             'class'       => $class_name,
  216.             'hook'        => $action_name,
  217.             'method'      => $method_name,
  218.             'type'        => $type,
  219.             'args'        => $this->sanitize_args_for_json( $args ),
  220.             'user_id'     => get_current_user_id(),
  221.             'url'         => $_SERVER['REQUEST_URI'] ?? '',
  222.             'post_data'   => $this->sanitize_args_for_json( $_POST ),
  223.             'get_data'    => $this->sanitize_args_for_json( $_GET ),
  224.         );
  225.  
  226.         // Add backtrace for context.
  227.         $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 10 );
  228.         $capture_entry['backtrace'] = array_map( function( $trace ) {
  229.             return array(
  230.                 'file'     => isset( $trace['file'] ) ? basename( $trace['file'] ) : 'unknown',
  231.                 'line'     => $trace['line'] ?? 0,
  232.                 'function' => $trace['function'] ?? 'unknown',
  233.                 'class'    => $trace['class'] ?? '',
  234.             );
  235.         }, $backtrace );
  236.  
  237.         // Store the captured data.
  238.         $this->captured_data[] = $capture_entry;
  239.  
  240.         // For filters, return the original value.
  241.         return $value;
  242.     }
  243.  
  244.     /**
  245.      * Sanitize arguments for JSON encoding.
  246.      *
  247.      * @since x.x.x
  248.      *
  249.      * @param mixed $data Data to sanitize.
  250.      * @return mixed
  251.      */
  252.     private function sanitize_args_for_json( $data ) {
  253.        
  254.         if ( is_object( $data ) ) {
  255.             // Convert objects to arrays, but capture class name.
  256.             if ( method_exists( $data, 'to_array' ) ) {
  257.                 return array(
  258.                     '__object_class' => get_class( $data ),
  259.                     '__data'         => $data->to_array(),
  260.                 );
  261.             } else {
  262.                 return array(
  263.                     '__object_class' => get_class( $data ),
  264.                     '__data'         => get_object_vars( $data ),
  265.                 );
  266.             }
  267.         } elseif ( is_array( $data ) ) {
  268.             return array_map( array( $this, 'sanitize_args_for_json' ), $data );
  269.         } elseif ( is_resource( $data ) ) {
  270.             return '__resource__';
  271.         } else {
  272.             return $data;
  273.         }
  274.     }
  275.  
  276.     /**
  277.      * Dump all captured data to file for LLM analysis.
  278.      *
  279.      * @since x.x.x
  280.      */
  281.     public function dump_captured_data() {
  282.        
  283.         if ( empty( $this->captured_data ) ) {
  284.             return;
  285.         }
  286.  
  287.         // Prepare final dump structure.
  288.         $dump_data = array(
  289.             'capture_session' => array(
  290.                 'start_time'      => current_time( 'mysql' ),
  291.                 'total_captures'  => count( $this->captured_data ),
  292.                 'monitored_classes' => $this->monitored_integrations,
  293.                 'discovered_actions' => $this->target_actions,
  294.             ),
  295.             'captures' => $this->captured_data,
  296.         );
  297.  
  298.         // Write to file.
  299.         file_put_contents(
  300.             $this->dump_file,
  301.             json_encode( $dump_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ),
  302.             LOCK_EX
  303.         );
  304.  
  305.         // Also create a summary for quick analysis.
  306.         $this->create_summary_file( $dump_data );
  307.     }
  308.  
  309.     /**
  310.      * Create a summary file for quick LLM analysis.
  311.      *
  312.      * @since x.x.x
  313.      *
  314.      * @param array $dump_data Full dump data.
  315.      */
  316.     private function create_summary_file( $dump_data ) {
  317.        
  318.         $summary = array(
  319.             'session_info' => $dump_data['capture_session'],
  320.             'hook_frequency' => array(),
  321.             'class_activity' => array(),
  322.             'user_interactions' => array(),
  323.         );
  324.  
  325.         // Analyze the captured data.
  326.         foreach ( $dump_data['captures'] as $capture ) {
  327.             // Hook frequency.
  328.             $hook_key = $capture['hook'];
  329.             if ( ! isset( $summary['hook_frequency'][ $hook_key ] ) ) {
  330.                 $summary['hook_frequency'][ $hook_key ] = 0;
  331.             }
  332.             $summary['hook_frequency'][ $hook_key ]++;
  333.  
  334.             // Class activity.
  335.             $class_key = $capture['class'];
  336.             if ( ! isset( $summary['class_activity'][ $class_key ] ) ) {
  337.                 $summary['class_activity'][ $class_key ] = array();
  338.             }
  339.             if ( ! isset( $summary['class_activity'][ $class_key ][ $hook_key ] ) ) {
  340.                 $summary['class_activity'][ $class_key ][ $hook_key ] = 0;
  341.             }
  342.             $summary['class_activity'][ $class_key ][ $hook_key ]++;
  343.  
  344.             // User interactions.
  345.             $user_id = $capture['user_id'];
  346.             if ( ! isset( $summary['user_interactions'][ $user_id ] ) ) {
  347.                 $summary['user_interactions'][ $user_id ] = 0;
  348.             }
  349.             $summary['user_interactions'][ $user_id ]++;
  350.         }
  351.  
  352.         // Sort by frequency.
  353.         arsort( $summary['hook_frequency'] );
  354.        
  355.         // Write summary.
  356.         file_put_contents(
  357.             str_replace( '.json', '-summary.json', $this->dump_file ),
  358.             json_encode( $summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ),
  359.             LOCK_EX
  360.         );
  361.     }
  362.  
  363.     /**
  364.      * Get captured data (for debugging).
  365.      *
  366.      * @since x.x.x
  367.      *
  368.      * @return array
  369.      */
  370.     public function get_captured_data() {
  371.         return $this->captured_data;
  372.     }
  373.  
  374.     /**
  375.      * Clear captured data.
  376.      *
  377.      * @since x.x.x
  378.      */
  379.     public function clear_captured_data() {
  380.         $this->captured_data = array();
  381.     }
  382.  
  383.     /**
  384.      * Enable/disable specific integration monitoring.
  385.      *
  386.      * @since x.x.x
  387.      *
  388.      * @param string $class_name Integration class name.
  389.      * @param bool   $enable     Enable or disable monitoring.
  390.      */
  391.     public function toggle_integration_monitoring( $class_name, $enable = true ) {
  392.         if ( $enable && ! in_array( $class_name, $this->monitored_integrations ) ) {
  393.             $this->monitored_integrations[] = $class_name;
  394.         } elseif ( ! $enable ) {
  395.             $this->monitored_integrations = array_diff( $this->monitored_integrations, array( $class_name ) );
  396.         }
  397.     }
  398. }
  399.  
  400. // Initialize the scraper.
  401. new WPF_Integration_Scraper();
  402.  
Advertisement
Add Comment
Please, Sign In to add comment