質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
cURL

cURLはHTTP, FTPやTelnetなど複数のプロトコルを用いてデータを転送するライブラリとコマンドラインツールを提供します。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

Q&A

1回答

520閲覧

AJAXで`curl_multi_init`の結果を、順次HTMLへ出力することは可能でしょうか?

nippon

総合スコア0

cURL

cURLはHTTP, FTPやTelnetなど複数のプロトコルを用いてデータを転送するライブラリとコマンドラインツールを提供します。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Ajax

Ajaxとは、Webブラウザ内で搭載されているJavaScriptのHTTP通信機能を使って非同期通信を利用し、インターフェイスの構築などを行う技術の総称です。XMLドキュメントを指定したURLから読み込み、画面描画やユーザの操作などと並行してサーバと非同期に通信するWebアプリケーションを実現することができます。

0グッド

0クリップ

投稿2020/08/24 19:25

前提・実現したいこと

curl_multi_initで並列してレスポンスを取得し、それを順次HTMLへ出力する方法を知りたいです。
宜しくお願い致します。

発生している問題・エラーメッセージ

curl_multi_initで並列してレスポンスを取得する処理を実装したところなのですが、先の疑問が生じました。
これをHTMLに順次出力するにはどうしたらいいのか?という疑問です。

順次出力できないなら、せっかく並列で取得したレスポンスなのに、意味ないじゃん!と思ったのですが…方法が思いつきません。

該当のソースコード

例えば次のJSは、buttonのクリックでPHPのsearch_allが発動します。search_allが複数のcURLを平行して実行するので、そのレスポンスを次のJSのresponseで順次HTMLへ出力、ということをイメージしています。

jQeury

1$(document).on('click','button',function() { 2 $.ajax({ 3 url : 'http://www...', 4 type: 'POST', 5 data: { action: 'search_all' } 6 }).done( function( response ) { 7 // ここでPHPの search_all の結果を順次受け取り、HTMLに出力したい 8 }); 9});

次がsearch_allになります。コメントでJSに返すとあるところで、上記JSのresponseに返したいのですが…

PHP

1function search_all(){ 2 3 // curl_multiハンドラを用意 4 $TIMEOUT = 10; 5 $mh = curl_multi_init(); 6 7 // 送信データ 8 $data = array( 9 array( 'name' => 'a', 'data' => 1 ), 10 array( 'name' => 'b', 'data' => 2 ), 11 ); 12 13 // 送信データごとにcURLを実行 14 foreach ($data as $d) { 15 $ch = curl_init(); 16 curl_setopt_array($ch, array( 17 CURLOPT_URL => 'http://www...', 18 CURLOPT_RETURNTRANSFER => true, 19 CURLOPT_TIMEOUT => $TIMEOUT, 20 CURLOPT_CONNECTTIMEOUT => $TIMEOUT, 21 CURLOPT_HTTPHEADER => array('Content-Type: application/json'), 22 CURLOPT_CUSTOMREQUEST => 'POST', 23 CURLOPT_POSTFIELDS => json_encode($d, JSON_PRETTY_PRINT) 24 )); 25 curl_multi_add_handle($mh, $ch); 26 } 27 28 // リクエストを開始する 29 do { 30 $stat = curl_multi_exec($mh, $running); //multiリクエストスタート 31 } while ($stat === CURLM_CALL_MULTI_PERFORM); 32 33 // レスポンスをcurl_multi_selectで待つ 34 do switch (curl_multi_select($mh, $TIMEOUT)) { //イベントが発生するまでブロック 35 36 case -1: //selectに失敗 37 usleep(10); //ちょっと待ってからretry 38 do { 39 $stat = curl_multi_exec($mh, $running); 40 } while ($stat === CURLM_CALL_MULTI_PERFORM); 41 continue 2; 42 43 case 0: //タイムアウト 44 continue 2; //ここではcontinueでリトライ 45 46 default: //どれかが成功 or 失敗した 47 do { 48 $stat = curl_multi_exec($mh, $running); //ステータスを更新 49 } while ($stat === CURLM_CALL_MULTI_PERFORM); 50 51 do if ($raised = curl_multi_info_read($mh, $remains)) { 52 53 // JSに返す 54 $response = curl_multi_getcontent($raised['handle']); 55 if ($response === false) { 56 echo 'ERROR!!!'; 57 } else { 58 echo json_decode($response, true); 59 } 60 61 curl_multi_remove_handle($mh, $raised['handle']); 62 curl_close($raised['handle']); 63 } while ($remains); 64 65 } while ($running); 66 curl_multi_close($mh); 67}

試したこと

根本的に、一回のAJAXのリクエストで、「PHPの結果を順次JSに返す」ことなどできないだろう、という考えを持っています。

で結局、「PHPの結果を順次JSに返す」方法が思いつきませんので、ひとまずはcurl_multi_initでなく普通のcurl_initにして、jQeury側で以下のように非同期に複数実行し、順次その結果を出力していく手はずでできました。

jQeury

1function search_single(){ 2 // curl_initを実行する関数 3} 4 5var func = $.when( search_single() ).done( function( response ){ 6 // ここで順次HTMLに出力する 7}); 8funcs.push( func ); 9 10$.when.apply(null, funcs).then( 11 // 順次出力が全部終わったときの処理 12);

このように、まぁギリギリできましたので最悪これでもいいのですが、やはりcurl_multi_initを使っての方法が気になっています。

それができなければ何のためのcURLの並列なのか?と疑問だからです。PHP側の処理は並列で早くはなりますが、結局HTMLへの出力がまとめて一回でしかできないのなら意味ないじゃん!っていう感じがします。

なのでcurl_multi_initで並列してレスポンスを取得し、それを順次HTMLへ出力する方法を知りたい。となったのですが、いかがなものでしょうか。

補足情報(FW/ツールのバージョンなど)

なし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

せっかく並列で取得したレスポンスなのに、意味ないじゃん!

そんなことはありません。1つ1秒のcurlを3本取得するとき
非同期処理なら1秒ですが、同期なら3秒かかります

投稿2020/08/25 01:31

編集2020/08/25 01:39
yambejp

総合スコア114769

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2020/08/25 01:32

PHP側の処理として <?PHP set_time_limit( 0 ); for($i=0;$i<3;$i++){ sleep(1); print $i; @ob_flush(); @flush(); } みたいなバッファを貯めずに出力してみてください。
yambejp

2020/08/25 01:33

これを、jsで非同期に受け取るにはこう fetch(url).then(res => { const reader = res.body.getReader(); const stream = new Response( new ReadableStream({ start(controller) { (async()=>{ let result = await reader.read(); while (!result.done) { const value = result.value; console.log(new TextDecoder().decode(value)); controller.enqueue(value); result = await reader.read(); } controller.close(); })(); } }) ); return stream; }).then(res=>res.text()).then(res=>{console.log("end")});
nippon

2020/08/25 13:08

できるんですか?! 一瞬期待しましたが、残念。 コンソールの表示ですが、1秒ずつ次の表示がされると思ったのですが、 0 1 2 end しかしこうではなく、3秒待ってから一気に 012 end となり、1秒ずつにはならなかったです。 ob_fhash調べてみますとできそうな感触はしたんですけどねぇ… まぁ質問みたいに連続でJSをリクエストするよりも、setIntervalで定期的に見に行く方がまだマシですかね。
nippon

2020/08/25 13:35

マニュアル https://www.php.net/manual/ja/function.flush.php に > flush() はウェブサーバーのバッファリング手法を上書きすることはできません とあるので、このせいでしょうかね。うーんレンタルサーバー残念すぎる。
yambejp

2020/08/26 00:55

apache、phpのバージョン、ブラウザの種類によって若干バッファの考え方が 違う場合があるので微妙ですね ダミーでスペースを大量に吐いてやるというのもよくある手段です <?PHP set_time_limit( 0 ); print str_repeat(" ",1024); for($i=0;$i<3;$i++){ @ob_flush(); @flush(); sleep(1); print $i; } flush処理が先に来ているのは最初にダミーをはいているからです
yambejp

2020/08/26 00:55

受け取り側もダミー文字列がきたときは無視しないといけないのでこう fetch(url).then(res => { const reader = res.body.getReader(); const stream = new Response( new ReadableStream({ start(controller) { (async()=>{ let result = await reader.read(); while (!result.done) { const value = result.value; const v=new TextDecoder().decode(value); if(!/^\s{1024}$/.test(v)){ console.log(v); } controller.enqueue(value); result = await reader.read(); } controller.close(); })(); } }) ); return stream; }).then(res=>res.text()).then(res=>{console.log("end")});
nippon

2020/08/26 13:55 編集

そういうテクニックもあるんですか。たぶんレンタルサーバーのせいでそれもできませんでしたけど、めちゃ勉強になりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問