独自いいねボタン
独自いいねボタンを実装して実現したいこと
ページ別に独自いいねボタンがクリックされた回数を表示したい
完成版デモ
カスタマイズ版特別デモ
(カスタマイズ版デモではクリック回数が1万回を超えると・・・)
使う言語とか
- Ajax(エイジャックス)
- PHP(ピーエイチピー)
- JSON(ジェイソン)
- JaveScript(ジャバスクリプト)
- jQuery(ジェイクエリー)
- HTML(エイチティーエムエル)
仕組みの概要
初期表示時
目的
そのページが何回いいねボタンを押されたかを取得する
- [jQuery] Ajax通信する
- [PHP] JSONデータ読み込む
- [PHP] 読み込んだ数値(いいねボタンを押された回数)を返す
- [jQuery] 返ってきた数値をそのまま表示する
いいねボタンクリック時
目的
そのページがクリックされた回数を+1して保存する
- [jQuery] Ajax通信する
- [PHP] JSONデータ読み込む
- [PHP] 読み込んだ数値に+1してJSONに保存する
- [PHP] +1した数値を返す
- [jQuery] 返ってきたデータをそのまま表示する
※1人で何回押してもクリック数がカウントアップする
処理のフロー
こんなダサいフローチャート・・・プログラミングしかできないエンジニアなのでこんなもんです!エクセル万歳。
気になることとか
- Q.ボタンってどうやって作るの?
- Q.ディレクトリ構成どうする?
- Q.PHPにPOSTでAjax通信する場合の脆弱性対策どうする?
- Q.JSONを使う場合のインジェクションやXSSは大丈夫?
- Q.JSONPじゃなくてJSONでいいの?
- Q.JSONの内容が見づらいけど大丈夫?
気になることを解消していこう
Q.ボタンってどうやって作るの?
A.
スキルが無いならジェネレーターで!
というわけで私はジェネレーター使います。
WEBジェネレーター!
こちらを使いました。
2_5dbutton
いやー、実にいい時代ですね。
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パーサを使って閲覧すれば超見やすい!
こんな感じで使います。
jq '.' data.json
じゃあ作ってみる
どこから取り掛かる?
順番的には、
- PHPのコーディング
- ボタン作成(ジェネレーター利用)
- HTMLのコーディング
- 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をコピーして保存します。
ファイル名を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万回を超えると・・・)