• WEB

JSONを使って独自いいねボタンを作ってみた

  • まごころエンジニア
    まごころエンジニア システムちーむ
  • このエントリーをはてなブックマークに追加
JSONを使って独自いいねボタンを作ってみた

独自いいねボタン

独自いいねボタンを実装して実現したいこと

ページ別に独自いいねボタンがクリックされた回数を表示したい

完成版デモ
カスタマイズ版特別デモ
(カスタマイズ版デモではクリック回数が1万回を超えると・・・)

使う言語とか

  • Ajax(エイジャックス)
  • PHP(ピーエイチピー)
  • JSON(ジェイソン)
  • JaveScript(ジャバスクリプト)
  • jQuery(ジェイクエリー)
  • HTML(エイチティーエムエル)

仕組みの概要

初期表示時

目的
そのページが何回いいねボタンを押されたかを取得する

  1. [jQuery] Ajax通信する
  2. [PHP] JSONデータ読み込む
  3. [PHP] 読み込んだ数値(いいねボタンを押された回数)を返す
  4. [jQuery] 返ってきた数値をそのまま表示する

いいねボタンクリック時

目的
そのページがクリックされた回数を+1して保存する

  1. [jQuery] Ajax通信する
  2. [PHP] JSONデータ読み込む
  3. [PHP] 読み込んだ数値に+1してJSONに保存する
  4. [PHP] +1した数値を返す
  5. [jQuery] 返ってきたデータをそのまま表示する
    ※1人で何回押してもクリック数がカウントアップする

処理のフロー

処理のフロー

こんなダサいフローチャート・・・プログラミングしかできないエンジニアなのでこんなもんです!エクセル万歳。

気になることとか

  • Q.ボタンってどうやって作るの?
  • Q.ディレクトリ構成どうする?
  • Q.PHPにPOSTでAjax通信する場合の脆弱性対策どうする?
  • Q.JSONを使う場合のインジェクションやXSSは大丈夫?
  • Q.JSONPじゃなくてJSONでいいの?
  • Q.JSONの内容が見づらいけど大丈夫?

気になることを解消していこう

Q.ボタンってどうやって作るの?

A.
スキルが無いならジェネレーターで!
というわけで私はジェネレーター使います。
WEBジェネレーター!

こちらを使いました。
2_5dbutton

25dbutton

いやー、実にいい時代ですね。
CSSやデザインがわからなくてもカッコいいものがポンっとできちゃいます。

Q.ディレクトリ構成どうする?

A.
じゃあこんな感じで


 iine/
 ├ css/
 ├ js/
 ├ index.php
 └ data.json
 

※CSSとJavasScriptのファイルは省略してます。

デモ用はこんな感じ


iine-page
 ├ index.html
 ├ page1.html
 ├ page2.html
 ├ apple/
 │ ├ page1.html
 │ └ page2.html
 ├ orange/
 │ ├ page1.html
 │ └ page2.html
 └ iine/
    ├ css/
    ├ font/
    ├ js/
    ├ index.php
    └ data.json
 

Q.PHPにPOSTでAjax通信する場合の脆弱性対策どうする?

A.
AjaxでPHPの実行を行う場合、どこのサイトからでもアクセスできてしまったら良くなさそう。
XSSとか。

というわけでどこからアクセスされたかをチェックする

Q.JSONを使う場合のインジェクションやXSSは大丈夫?

A.
とりあえずJSONなら大

Q.JSONPじゃなくてJSONでいいの?

A.
JSONPだと別サーバからのアクセスを許可してしまうから今回はやらない。
インジェクションの対策をしなくてはならないのでJSONPでやる場合はちゃんと下調べしよう。

Googleで「xss json jsonp」で検索すれば問題と対策に関するサイトやブログが出てきます。

今回はJSONでやることにする。

Q.JSONの内容が見づらいけど大丈夫?

A.
ブラウザのアドオンやjqというJSONパーサを使って閲覧すれば超見やすい!

JSONView
jq

こんな感じで使います。


jq '.' data.json

jq

じゃあ作ってみる

どこから取り掛かる?

順番的には、

  1. PHPのコーディング
  2. ボタン作成(ジェネレーター利用)
  3. HTMLのコーディング
  4. jQueryのコーディング

(思いつきで順番決めただけ・・・)

1. PHPのコーディング


// index.php
<?php

    #文字コード指定
    setlocale(LC_ALL,'ja_JP.UTF-8');

    #Ajax通信ではなく、直接URLを叩かれた場合は処理をしないようにしたい
    if (!(isset($_SERVER['HTTP_X_REQUESTED_WITH'])
            && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest')
        && (!empty($_SERVER['SCRIPT_FILENAME'])
            && basename($_SERVER['SCRIPT_FILENAME']) === 'index.php')
       ) {
        die();
    }

    #リファラーを使いたいので、入っていなければノーカウント
    $referer = htmlspecialchars($_SERVER['HTTP_REFERER'], ENT_QUOTES, 'UTF-8');
    if (!(isset($referer))) {
        exit;
    }

    #処理を判別(GET)
    $action = htmlspecialchars($_GET['act'], ENT_QUOTES, 'UTF-8');
    switch ($action) {
        case 'show':
            echo showJson($referer);
            break;
        case 'plus':
            echo plusJson($referer);
            break;
        default:
            exit;
    }
    exit;

    #JSON 読込処理
    function showJson($referer) {

        $jsonname = 'data.json';
        $json = file_get_contents($jsonname);
        $pluscnt = json_decode($json, true);
        $cnt = 0;
        foreach($pluscnt as $rkey => $row) {
            foreach($row as $key => $val) {
                if ($key == $referer) {
                    $cnt = $val;
                    break 2;
                }
            }
            break;
        }

        return $cnt;
    }

    #JSON 更新処理
    function plusJson($referer) {

        $jsonname = 'data.json';
        $json = file_get_contents($jsonname);
        $pluscnt = json_decode($json, true);

        $cnt = 0;
        foreach($pluscnt as $rkey => $row) {
            foreach($row as $key => $val) {
                if ($key == $referer) {
                    $cnt = $val + 1;
                    $pluscnt[$rkey][$key] = $cnt;
                    break 2;
                }
            }
            #今のページが見つからなかった場合
            $pluscnt[$rkey][$referer] = 1;
            $cnt = 1;
            break;
        }

        #JSONへ変換&上書き保存
        $handle = fopen($jsonname, 'w');
        fwrite($handle, json_encode($pluscnt));
        fclose($handle);

        return $cnt;
    }
?>

PHPの解説

a. AjaxのXSSを防ぐ為に、Ajax通信時のみPHPの処理を実行したい。


// Ajax通信ではなく、直接URLを叩かれた場合は処理をしないようにしたい
if (!(isset($_SERVER['HTTP_X_REQUESTED_WITH'])
        && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest')
    && (!empty($_SERVER['SCRIPT_FILENAME'])
        && basename($_SERVER['SCRIPT_FILENAME']) === 'index.php')
   ) {
    die();
}

Ajax通信時には$_SERVER[‘HTTP_X_REQUESTED_WITH’]にXmlHttpRequestが設定される。

だからXmlHttpRequestが入っているかをチェックします。
strtolower()で全部小文字にしてチェックするのが確実ですね。

こういうのを同定とか言います。
直接URLを実行してきた場合はXmlHttpRequestが抜けるので、こういった対策になります。

b.直接URLを実行された場合は何も起こらないようにしたい


die();

ここでは、die(‘直接URL叩くのは対応してません’)などとしてメッセージを出力することも可能。

ただ、それはテスト時に確認すればいいことで、完成時にはdieとかexitのみで。

直接URL叩いてくるような攻撃的な人にはできればメッセージなどでヒントを与えずに、

何をされたのかわからなかった状態にし
「何が起きたかわからなかった」状態にしたい。

だから、ここではdie()で処理を許可しない&メッセージ出しませんということを明示しているつもり。

c.リファラーが入っているかチェックしたい


// リファラーを使いたいので、入っていなければノーカウント
$referer = $_SERVER['HTTP_REFERER'];
if (!(isset($referer))) {
    exit;
}

言わずもがな!

この独自いいねボタンはページのURLをインデックスにしてクリック回数を管理することにしている。
だから、リファラーが無いとこのプログラムは意味のないものになってしまう。
だからリファラーの存在をチェックします。

ちょっと調べないと分からないなーといった点は以上でした。
はい。次、行きます。

2.ボタン作成(ジェネレーター利用)

さくっと作ってしまいましょう。

先述した2_5dBUTTONで作ります。
2_5dbutton

いろいろと自分好みに設定できたら、下部の右にあるCSSをコピーして保存します。

25dbutton2

ファイル名を2_5dbutton.cssにして保存。
下部の左にあるHTMLは後で使います。

一番下にある下矢印をクリックすると他に必要なJSやCSSがZIPでダウンロードできます。
これもダウンロードして展開しましょう。

展開後に必要なものを適所に設置していくと、こんな感じになります。


iine/
 ├ css/
 │ ├ layout.css
 │ └ 2_5dbutton.css
 ├ font/
 │ ├ LigatureSymbols-2.11.eot
 │ ├ LigatureSymbols-2.11.otf
 │ ├ LigatureSymbols-2.11.svg
 │ ├ LigatureSymbols-2.11.ttf
 │ └ LigatureSymbols-2.11.woff

fontファイルはボタンジェネレータで作成するアイコンが含まれていますので、アイコンが不要であれば無くても問題なく動作します。
他にもjsファイルが入っていますが今回は使わないでやります。

3.HTMLのコーディング

先ほどのボタンジェネレータで出力されていたHTMLをコピーしてきます。
こんな感じです。


<div class="general-button">
  <div class="button-content">
    <span class="icon-font">file</span>
    <span class="button-text">TEXT</span>
  </div>
</div>

ここで使われているModernizr.jsというのは使用ブラウザでJSが正常の動作するかチェックするものですので、今回は不要です。

基本的なHTMLテンプレートにはめ込んでHTMLのコーディングは終了。
こんな感じになりました。



<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <link rel="stylesheet" type="text/css" media="screen" href="./iine/css/2_5dbutton.css" />
  <link rel="stylesheet" type="text/css" media="screen" href="./iine/css/layout.css" />
  <script type="text/javascript" src="./iine/js/jquery-2.1.3.min.js"></script>
  <script type="text/javascript" src="./iine/js/plus.js"></script>

</head>
<body>
  <div class="general-button iine-btn">
    <div class="button-content">
      <span class="icon-font">good</span>
      <span class="button-text">よかった<div class="iine-cnt"></div></span>
    </div>
  </div>
</body>
</html>

4.jQueryのコーディング

jQueryでやりたいことは、初期表示時とボタンクリック時にAjaxでPHPを呼び出すこと

その為のコーディングになります。


// iine.js
//初期表示時の処理
$(document).ready(function() {
    //ajaxstartに'show'という文字列を渡す
    ajaxstart('show');
});

//いいねボタンがクリックされた場合の処理
$(function(){
    //class名:"iine-bnt"がクリックされたら処理する
    jQuery(".iine-btn").click(function() {
        //ajaxstartに'plus'という文字列を渡す
        ajaxstart('plus');
    });
});

function ajaxstart(act) {
    //Ajax通信処理
    $.ajax({
        type: "POST",
        url: "http://www.go-next.co.jp/blog/sample/2015/0116/iine-page/iine/index.php?act="+act,
        success: function(data){
            //返り値がある場合のみ処理
            if(data.length > 0){
                //返り値を指定class名:"iine-cnt"の値に差し込む
                $(".iine-cnt").text("→ "+data);
            }
        }
    });
}

ここまででできたディレクトリ構成

こんな感じになったはず。


iine/
 ├ css/
 │ ├ layout.css
 │ └ 2_5dbutton.css
 ├ font/
 │ ├ LigatureSymbols-2.11.eot
 │ ├ LigatureSymbols-2.11.otf
 │ ├ LigatureSymbols-2.11.svg
 │ ├ LigatureSymbols-2.11.ttf
 │ └ LigatureSymbols-2.11.woff
 ├ js/
 │ └ iine.js
 └ index.php

よく見てみると大事なものを忘れていました。
そう、JSONデータです。

とりあえずでdata.jsonを作成して置いておきます。
中身は[[]]としておきます。


// data.json
[[]]

そうすると下記のようなディレクトリ構成になりますね。


iine/
 ├ css/
 │ ├ layout.css
 │ └ 2_5dbutton.css
 ├ font/
 │ ├ LigatureSymbols-2.11.eot
 │ ├ LigatureSymbols-2.11.otf
 │ ├ LigatureSymbols-2.11.svg
 │ ├ LigatureSymbols-2.11.ttf
 │ └ LigatureSymbols-2.11.woff
 ├ js/
 │ └ iine.js
 │ index.php
 └ data.json

デモ

ここまで作ってきたものを動作検証する為に、1ページのみでなく同じようなページを複数作ってみました。

ディレクトリ構成は下記のようになりました。


iine-page
 ├ index.html
 ├ page1.html
 ├ page2.html
 ├ apple/
 │ ├ page1.html
 │ └ page2.html
 ├ orange/
 │ ├ page1.html
 │ └ page2.html
 └ iine/
    ├ css/
    │ ├ layout.css
    │ └ 2_5dbutton.css
    ├ font/
    │ ├ LigatureSymbols-2.11.eot
    │ ├ LigatureSymbols-2.11.otf
    │ ├ LigatureSymbols-2.11.svg
    │ ├ LigatureSymbols-2.11.ttf
    │ └ LigatureSymbols-2.11.woff
    ├ js/
    │ └ iine.js
    │ index.php
    └ data.json

それではこちらのページからデモをお試しください。

デモページ
カスタマイズ版特別デモ
(カスタマイズ版デモではクリック回数が1万回を超えると・・・)

このエントリーをはてなブックマークに追加

まごころエンジニアが最近書いた記事

WRITERS POSTS もっと見る

他にもこんな記事が読まれています!

  • WEB
  • マーケティング
  • サーバー・ネットワーク
  • ライフスタイル
  • お知らせ