$value ) { $new_key = self::replace_variables( $key, $collection_vars, $env_vars, $global_vars ); $new_value = self::replace_variables( $value, $collection_vars, $env_vars, $global_vars ); $result[ $new_key ] = $new_value; } return $result; } /** * Get all active variables (merged by scope). * * @param array $collection_vars Optional collection variables. * @return array Merged variables. */ public static function get_active_variables( $collection_vars = array() ) { $global_vars = get_option( 'aradt_global_variables', array() ); if ( ! is_array( $global_vars ) ) { $global_vars = array(); } $env_vars = array(); $active_env_id = get_option( 'aradt_active_environment', '' ); $environments = get_option( 'aradt_environments', array() ); if ( ! empty( $active_env_id ) && is_array( $environments ) ) { foreach ( $environments as $env ) { if ( isset( $env['id'] ) && $env['id'] === $active_env_id && isset( $env['variables'] ) ) { $env_vars = $env['variables']; break; } } } if ( ! is_array( $collection_vars ) ) { $collection_vars = array(); } return array_merge( $global_vars, $env_vars, $collection_vars ); } /** * Sanitize a JSON string and decode it safely. * * @param string $json Raw JSON string. * @return mixed Decoded value or null on failure. */ public static function safe_json_decode( $json ) { if ( empty( $json ) || ! is_string( $json ) ) { return null; } $decoded = json_decode( wp_unslash( $json ), true ); if ( json_last_error() !== JSON_ERROR_NONE ) { return null; } return $decoded; } /** * Verify current user has plugin capability. * * @return bool */ public static function current_user_can_use() { return current_user_can( 'manage_options' ); } /** * Standard AJAX permission + nonce check. * * @param string $nonce_action The nonce action name. * @return void Dies on failure. */ public static function verify_ajax_request( $nonce_action = 'aradt_nonce' ) { if ( ! self::current_user_can_use() ) { wp_send_json_error( array( 'message' => esc_html__( 'Insufficient permissions.', 'advanced-rest-api-devtools' ) ), 403 ); } check_ajax_referer( $nonce_action, 'nonce' ); } /** * Generate code snippet for a request. * * @param string $method HTTP method. * @param string $url Request URL. * @param array $headers Headers key-value. * @param string $body Request body. * @param string $format Output format: curl, php, javascript. * @return string */ public static function generate_code( $method, $url, $headers = array(), $body = '', $format = 'curl' ) { switch ( $format ) { case 'curl': return self::generate_curl( $method, $url, $headers, $body ); case 'php': return self::generate_php( $method, $url, $headers, $body ); case 'javascript': return self::generate_javascript( $method, $url, $headers, $body ); default: return ''; } } /** * Generate cURL command. */ private static function generate_curl( $method, $url, $headers, $body ) { $parts = array( "curl -X {$method}" ); $parts[] = " '{$url}'"; if ( is_array( $headers ) ) { foreach ( $headers as $key => $value ) { $parts[] = " -H '{$key}: {$value}'"; } } if ( ! empty( $body ) && in_array( $method, array( 'POST', 'PUT', 'PATCH' ), true ) ) { $parts[] = " -d '" . addcslashes( $body, "'" ) . "'"; } return implode( " \\\n", $parts ); } /** * Generate PHP wp_remote_request code. */ private static function generate_php( $method, $url, $headers, $body ) { $code = " '{$method}',\n"; if ( ! empty( $headers ) && is_array( $headers ) ) { $code .= " 'headers' => array(\n"; foreach ( $headers as $key => $value ) { $code .= " '{$key}' => '{$value}',\n"; } $code .= " ),\n"; } if ( ! empty( $body ) && in_array( $method, array( 'POST', 'PUT', 'PATCH' ), true ) ) { $code .= " 'body' => '{$body}',\n"; } $code .= " )\n"; $code .= ");\n\n"; $code .= "\$body = wp_remote_retrieve_body( \$response );\n"; $code .= "\$code = wp_remote_retrieve_response_code( \$response );\n"; return $code; } /** * Generate JavaScript fetch code. */ private static function generate_javascript( $method, $url, $headers, $body ) { $code = "const response = await fetch('{$url}', {\n"; $code .= " method: '{$method}',\n"; if ( ! empty( $headers ) && is_array( $headers ) ) { $code .= " headers: {\n"; foreach ( $headers as $key => $value ) { $code .= " '{$key}': '{$value}',\n"; } $code .= " },\n"; } if ( ! empty( $body ) && in_array( $method, array( 'POST', 'PUT', 'PATCH' ), true ) ) { $code .= " body: JSON.stringify(" . $body . "),\n"; } $code .= "});\n\n"; $code .= "const data = await response.json();\n"; $code .= "console.log(data);\n"; return $code; } /** * Run basic assertions against a response. * * @param string $script Assertion script text. * @param int $status Response status code. * @param string $body Response body. * @param array $headers Response headers. * @return array Array of assertion results. */ public static function run_assertions( $script, $status, $body, $headers = array() ) { if ( empty( $script ) ) { return array(); } $results = array(); $json = json_decode( $body, true ); $lines = explode( "\n", $script ); foreach ( $lines as $line ) { $line = trim( $line ); if ( empty( $line ) || str_starts_with( $line, '//' ) ) { continue; } // Parse: pm.test("name", () => { assertion }) if ( preg_match( '/pm\.test\(\s*["\'](.+?)["\']\s*,/', $line, $matches ) ) { $test_name = $matches[1]; $passed = false; // Status check: pm.response.to.have.status(200) if ( preg_match( '/\.status\(\s*(\d+)\s*\)/', $line, $status_match ) ) { $passed = ( (int) $status === (int) $status_match[1] ); } // JSON path exists: pm.response.to.have.jsonBody("key") elseif ( preg_match( '/\.jsonBody\(\s*["\'](.+?)["\']\s*\)/', $line, $json_match ) ) { $passed = ( is_array( $json ) && array_key_exists( $json_match[1], $json ) ); } // Body contains: pm.response.to.include("text") elseif ( preg_match( '/\.include\(\s*["\'](.+?)["\']\s*\)/', $line, $include_match ) ) { $passed = ( str_contains( $body, $include_match[1] ) ); } // JSON value equals: pm.expect(pm.response.json().key).to.eql(value) elseif ( preg_match( '/pm\.expect\(.*\.json\(\)\.(\w+)\)\.to\.eql\(\s*(.+?)\s*\)/', $line, $eql_match ) ) { $key = $eql_match[1]; $expected_raw = trim( $eql_match[2], '"\'' ); if ( is_array( $json ) && isset( $json[ $key ] ) ) { $passed = ( (string) $json[ $key ] === $expected_raw ); } } // Response time: pm.response.responseTime < 500 elseif ( preg_match( '/responseTime\s*<\s*(\d+)/', $line, $time_match ) ) { $results[] = array( 'name' => $test_name, 'passed' => null, 'info' => esc_html__( 'Response time assertion — skipped (not available in assertion context).', 'advanced-rest-api-devtools' ), ); continue; } else { $results[] = array( 'name' => $test_name, 'passed' => null, 'info' => esc_html__( 'Unrecognized assertion pattern.', 'advanced-rest-api-devtools' ), ); continue; } $results[] = array( 'name' => $test_name, 'passed' => $passed, ); } } return $results; } /** * Recursively sanitize an array or string. * * @param mixed $data Data to sanitize. * @return mixed Sanitized data. */ public static function sanitize_recursive( $data ) { if ( is_array( $data ) ) { return array_map( array( __CLASS__, 'sanitize_recursive' ), $data ); } if ( is_string( $data ) ) { return sanitize_text_field( $data ); } return $data; } }