第7章 名簿検索システムを作ろう-3
前章で、名簿検索システムを作成しました。
本章では、さらにキーワード検索機能を追加してみましょう

7-1. 検索フォームの改造

本章では、前章で作成した検索プログラムに、キーワード検索機能をプラスしてみたいと思います。
検索の操作機能は、第2ステップとして、IDのソートと性別による抽出の他に、キーワード検索を追加します。
■ 名簿検索システム「仕様」(第2ステップ)
設計項目 内容
検索機能 1. IDソート(昇順/降順)
2. 性別による抽出
3. キーワード検索
フォーム
name/value値
1. IDソート : name「id」, value:昇順「1」降順「2」
2. 性別 : name「sex」, value:両方「0」, 男性のみ「1」, 女性のみ「2」
3. キーワード : name「word」
文字コード UTF-8
検索プログラム名 member2.cgi
上記の「仕様」に基づき、データベース検索のための入力フォームを変更します。
HTMLの文字コードは、同様にUTF-8です。
ファイル名はindex.htmlで保存することにします。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>名簿データベース</title>
<style>
table { border-collapse:collapse; margin:1em 0; }
td,th { border:1px solid #555; padding:8px; }
</style>
</head>
<body>

<form action="member2.cgi">
<table>
<tr>
	<td>ソート</td>
	<td>
		<input type="radio" name="sort" value="1" checked>ID順(昇順)
		<input type="radio" name="sort" value="2">ID順(降順)
	</td>
</tr><tr>
	<td>男女別</td>
	<td>
		<input type="radio" name="sex" value="0" checked>両方
		<input type="radio" name="sex" value="1">男性のみ
		<input type="radio" name="sex" value="2">女性のみ
</tr><tr>
	<td>キーワード</td>
	<td><input type="text" name="word" size="25"></td>
	</td>
</tr>
</table>
<input type="submit" value="検索">
</form>

</body>
</html>
pagetop

7-2. 検索プログラムの改造

キーワード検索の追加にあたって、ポイントは二点あります。
第1には、キーワード検索にあたり、検索対象のカラムは、「名前 (name)」と「ふりがな (kana)」にします。
したがって、select文は「部分一致」になるため、キーワードの変数を$wordとしたとき、次のような構文になります。
select * from member name like '%$word%' or kana like '%$word%';
上記の構文をベースに、ID順のソートと性別抽出の条件を組み合わせます。
ポイント-1
検索対象のカラム(複数)を定義して、select文へはlike句をorで結ぶ。
第2には、セキュリティ上の汚染チェックです。キーワード検索の文字列は日本語になるため、危険な文字列だけを排除する必要があります。
キーワード検索のための入力値については、排除する記号を次のとおりとします。
排除する文字 (注意すべき特殊記号)
[タブ] [改行] [スペース] ! " # $ % & \ ' ( ) * + , - . / : ; < > = ? @ [ ] \ ^ _ ` { } | ~
参考サイト : セキュアプログラミング講座 > コマンド注入攻撃対策
ポイント-2
特に日本語等の引数をselect文へ定義する場合は、注意すべき特殊記号を排除する。
以上を留意して、第2ステップの検索プログラムを次のように記述してみます。
文字コードは同様にUTF-8になり、ファイル名はmember2.cgiとします。
#!/usr/local/bin/perl

# 名簿検索システム-2
# created by (c)kentweb

use strict;
use CGI::Carp qw(fatalsToBrowser);
use DBI;
use CGI;
my $cgi = new CGI;

# 引数を受取り
my $sort = $cgi->param('sort');
my $sex  = $cgi->param('sex');
my $word = $cgi->param('word');

# 引数の正当性
$sort =~ s/\D//g;
$sex  =~ s/\D//g;
$word =~ s/[<>&"'\r\s!#$\%()*+,\-.\/:;=?@\[\]\\^_`{}|~]//g;

# select文
my $select = "select * from member";

# 性別条件
my $flg;
if ($sex == 1) {
	$flg++;
	$select .= " where sex = '男'";
} elsif ($sex == 2) {
	$flg++;
	$select .= " where sex = '女'";
}

# キーワード
if ($word ne '') {
	if ($flg) {
		$select .= " and (";
	} else {
		$select .= " where";
	}
	$select .= " name like '%$word%' or kana like '%$word%'";
	$select .= ")" if ($flg);
}

# ソート条件
if ($sort == 1) {
	$select .= " order by id asc;";
} else {
	$select .= " order by id desc;";
}

# DB接続
my $dbh = DBI->connect("dbi:SQLite:dbname=member.db");

# 命令実行
my $sth = $dbh->prepare($select);
$sth->execute;

header();
print <<EOM;
<div align="center">
<table>
<tr>
	<th>会員ID</th>
	<th>名前</th>
	<th>ふりがな</th>
	<th>性別</th>
	<th>住所</th>
</tr>
EOM

# データ抽出
while (my ($id,$name,$kana,$sex,$addr) = $sth->fetchrow_array) {
	print qq|<tr><td>$id</td>|;
	print qq|<td>$name</td>|;
	print qq|<td>$kana</td>|;
	print qq|<td>$sex</td>|;
	print qq|<td>$addr</td></tr>\n|;
}

# 完了
$sth->finish();
undef $sth;
$dbh->disconnect;

print <<EOM;
</div>
</body>
</html>
EOM

#-----------------------------------------------------------
#  ヘッダー
#-----------------------------------------------------------
sub header {
	print <<EOM;
Content-type: text/html; charset=utf-8

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>名簿データベース</title>
<style>
table { border-collapse:collapse; margin:1em 0; }
td,th { border:1px solid #555; padding:8px; }
th { background:#ccc; }
</style>
</head>
<body>
EOM
}
上記のプログラムは、以下の名簿検索フォームから試すことができます。
ソート ID順(昇順) ID順(降順)
男女別 両方 男性のみ 女性のみ
キーワード
pagetop

7-3. 本番サーバ設置の場合

ディレクトリ/ファイルの設置方法ですが、前章ではXAMPP環境の設置例を示しましたが、実際の本番環境へ設置する場合の設置例について解説します。
本番環境で特に気を付けることとしては、名簿データベースである「member.db」を外部からダウンロードされないように注意することが必要です。
パターン1
基本は、次のように、公開ディレクトリであるホームディレクトリと「平行の位置」にデータ用ディレクトリを作成して、外部から直接アクセスできない位置に置きます。
かっこ内はパーミッション値です。
/var/www/  ← ログインする位置
      |
      +-- data / member.db [644]  ← 外部からアクセスされない位置に置く
      |
      +-- html / ... ホームディレクトリ
           |
           +-- member / index.html .......... 検索フォーム
                        member2.cgi [755] ... 検索プログラム
パターン2
サーバによっては、ログインする位置がホームディレクトリになっているところもあります。
その場合には、すべてが公開ディレクトリとなるため、パターン1の配置はできません。
このようなサーバでは、直下にデータ用ディレクトリを作成して、アクセスを排除するための.htaccessを一緒に置きます。
/var/www/html/  ← ログインする位置(ホームディレクトリ)
          |
          |
          +-- member / index.html .... 検索フォーム
                |      member2.cgi [755] ... 検索プログラム
                |
                +-- data / .htaccess   ......... ディレクトリ内へのアクセスを排除
                           member.db [644] ..... データファイル
.htaccessファイルの中身は、次の一文を入力したものとします。末尾は改行を入力してください。
deny from all
pagetop