ryoakg
6/3/2016 - 10:51 PM

JSONP をサーバサイドも含めて実装。 php -S 8080 とかで簡単に試せる

JSONP をサーバサイドも含めて実装。 php -S 8080 とかで簡単に試せる

<?php
if (PHP_SAPI === 'cli') {
  echo 'Usage:' . PHP_EOL;
  echo "  run: php -S localhost:8080 {$argv[0]}" . PHP_EOL;
  echo '  browse http://localhost:8080/' . PHP_EOL;
  exit;
}

const JSONP_URL_PATH = '/jsonp';

// routing
if (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) === JSONP_URL_PATH){
  render_js();
  return;
}
render_html();

// more info
//   http://www.atmarkit.co.jp/ait/articles/0908/10/news087.html
//   https://mathiasbynens.be/notes/javascript-identifiers
//   http://stackoverflow.com/questions/1661197/what-characters-are-valid-for-javascript-variable-names
//
//   間違えるとXSSの原因になる
//   そういう意味で、関数名を固定にするメリットもある
function is_js_var_valid($js_var){
  return preg_match('#\A[_$a-zA-z][_$a-zA-z0-9]*\z#', $js_var);
}

function render_js(){
  $callback_js_function_name = filter_input(INPUT_GET, 'callback');
  if (! $callback_js_function_name ||
      is_js_var_valid($callback_js_function_name) !== 1) {
    exit();
  }


  // 本当は、この部分で条件によってデータを変える必用がある
  $data = array('temperature' => 23, 'location' => '北海道');


  // 以下のコードがブラウザ側で実行される
  $js_snippet = $callback_js_function_name . '(' . json_encode($data) . ');';
  // コードを渡すので
  // データを送る場合より自由度が高くて危険性が高い


  // http://d.hatena.ne.jp/kanonji/20110406/1302065168
  // IE7,8 だと application/javascript だとエラーになるらしい
  header('Content-Type: application/javascript;charset=UTF-8');
  echo $js_snippet;
}

function render_html(){
?>
  <html>
    <head>
      <script>
       // JSONP で呼んでもらう関数
       // サーバからデータが帰ってきた後の継続になる
       function local_temperature(data){
         var box = document.getElementById('temperature');
         box.textContent = '今日の' + data.location + 'の気温: ' + data.temperature + '度';
       }

       window.onload = function(){
         var script = document.createElement('script');


         // callback= の部分で呼んでもらう関数名を指定している
         // FB, Twitter の投稿表示だと固定の関数名を使っていた
         script.src = '<?php echo JSONP_URL_PATH; ?>' + '?callback=' + 'local_temperature';
         // 可変だと重複を回避できるけど、
         // 使う人は名前を指定する手間が増える。
         // 非プログラマも使うと考えれば、名前を長くして固定にするのが無難かもしれない


         document.body.appendChild(script);
         script.parentNode.removeChild(script);
       };
      </script>
    </head>
    <body>
      <div id="temperature"></div>
    </body>
  </html>
<?php } ?>