PHP多线程Curl调用社交平台API后,通过WordPress admin_ajax返回JSON至前端JS的问题排查
Let's break down what's causing your issues and how to fix them step by step:
The Root Cause
Your multi_thread_curl function fetches raw JSON strings from the Facebook/Instagram APIs, then you run json_encode($results) on that array of pre-encoded JSON. This leads to double JSON encoding—the original JSON gets escaped (hence the backslashes), and when your frontend tries to parse it, it ends up with invalid JSON that throws the "Unexpected token" error.
Additionally, your frontend Fetch call references an undefined headers variable, which might be causing unintended request behavior.
Step 1: Fix the PHP Double Encoding Issue
Modify your sns_display_post_action function to decode each API response before encoding the final result. This ensures you only encode the data once, producing clean, unescaped JSON:
function sns_display_post_action(){ $facebookID = get_option( 'facebook_page_id' ); $instagramID = get_option( 'instagram_profile_id' ); $instagramName = get_option('instagram_profile_name'); $optionArray = array( CURLOPT_RETURNTRANSFER => TRUE, CURLOPT_TIMEOUT => 10 ); $access_token = get_option('access_token'); $limitpost = get_option( 'post_count_number' ); $data1 = "https://graph.facebook.com/v14.0/$instagramID?fields=business_discovery.username($instagramName){id,name,profile_picture_url,media.limit($limitpost){id,owner,username,media_type,permalink,caption,timestamp,media_url,children{media_url}}}&access_token=$access_token"; $data2 = "https://graph.facebook.com/v14.0/$facebookID?fields=id,about,name,picture{url},posts.limit($limitpost){id,created_time,message,permalink_url,attachments{media_type,type,media,url,subattachments,unshimmed_url,description},full_picture}&access_token=$access_token"; $urlArray = array( $data1, $data2 ); $nThreads = 2; $results = multi_thread_curl($urlArray, $optionArray, $nThreads); // Decode raw API JSON responses to PHP arrays $parsed_results = []; foreach ($results as $key => $raw_json) { $parsed = json_decode($raw_json, true); // true = return associative array if ($parsed !== null) { $parsed_results[$key] = $parsed; } else { // Optional: Handle invalid JSON from API $parsed_results[$key] = ['error' => 'Failed to parse API response']; } } // Send proper JSON header to avoid MIME type conflicts header('Content-Type: application/json'); echo json_encode($parsed_results); exit(); } add_action( 'wp_ajax_sns_post_action', 'sns_display_post_action'); add_action( 'wp_ajax_nopriv_sns_post_action', 'sns_display_post_action');
Why This Works:
- We convert each raw JSON string from the API into a PHP array with
json_decode, so the finaljson_encodeonly processes native PHP data structures. - The
Content-Typeheader ensures WordPress sends the response with the correct MIME type, which helps the Fetch API interpret it properly.
Step 2: Fix the Frontend Fetch Call
Your current code uses an undefined headers variable. When using FormData, the browser automatically sets the correct Content-Type header (with the boundary), so we can clean this up and add basic error handling:
let params = new FormData(); params.append("action", sns_show_options.action); params.append("nonce", sns_show_options.nonce); fetch(sns_show_options.api, { method: 'POST', // admin_ajax requires POST requests body: params }) .then(response => { // Catch HTTP errors before parsing JSON if (!response.ok) { throw new Error(`Request failed! Status: ${response.status}`); } return response.json(); }) .then(res => { console.log(res); }) .catch(error => { console.error('Fetch error:', error); });
Key Fixes Here:
- Explicitly set
method: 'POST'since WordPress admin_ajax requires POST for action requests. - Removed the undefined
headersproperty—FormData handles this automatically. - Added error handling to catch HTTP issues or parsing failures, making debugging easier.
Step 3: Clean Up the multi_thread_curl Function (Optional)
Your current function leaves curl handles open (you commented out curl_multi_remove_handle), which can lead to resource leaks. Let's fix that:
function multi_thread_curl($urlArray, $optionArray, $nThreads) { $curlArray = array_chunk($urlArray, $nThreads, true); $results = []; $ch_prefix = 'ch_'; foreach($curlArray as $threads) { $mh = curl_multi_init(); $handles = []; foreach($threads as $thread=>$value) { $handle_name = $ch_prefix . $thread; $$handle_name = curl_init(); curl_setopt_array($$handle_name, $optionArray); curl_setopt($$handle_name, CURLOPT_URL, $value); $handles[$thread] = $$handle_name; curl_multi_add_handle($mh, $$handle_name); } $active = null; do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } foreach($threads as $thread=>$value) { $results[$thread] = curl_multi_getcontent($handles[$thread]); curl_multi_remove_handle($mh, $handles[$thread]); curl_close($handles[$thread]); // Close individual curl handle } curl_multi_close($mh); } return $results; }
This ensures all curl resources are properly closed after use, preventing potential memory issues.
内容的提问来源于stack exchange,提问作者Vinicius Chinen

