職案人

求職・歴史・仏教などについて掲載するつもりだが、自分の思いつきが多いブログだよ。適当に付き合って下さい。

リンクカウンター/perlのCGI講座

2018年04月25日 | perl
アクセスカウンター

【環境条件】
Eclipse 4.4(ルナ)
XAMPP 1.8.3 
Perlは既にインストール済み
CGIを使用する時は、必ずApachを起動する

■リンクカウンター
これまでに勉強した 例題1~例題4 へのリンクが張られています。夫々のページへジャンプする度に link.cgi が介在して、夫々のページへのアクセス回数がカウントアップされた後に、夫々のページが表示されます。リンクカウンターの仕掛けは、link.cgi にによって実現されていますが、link.html を見ている人には、このような裏の仕掛けは見えません。

では、最初の画面「link.html」を表示させて見る。

→リンクが貼られた例題1~例題4が表示されたら、どれでも好きな所を選んで見る。
1)例題1


2)例題2


3)例題3


4)例題4


次にリンクカウンターが仕掛けられた閲覧記録画面「link.cgi」を起動する

閲覧記録画面
パス入力


閲覧記録


■スクリプト
リンクカウンターでは、2つのファイルを使う。一つは、リンクを貼ったlink.htmlと、リンクカウンターの仕掛けがあるlink.cgiである。

スクリプト「link.html」
1.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2.<html>
3.<head>
4.<meta http-equiv="content-type" content="text/html; charset=UTF-8">
5.<title>リンクのページ</title>
6.</head>
7.<body link="blue" vlink="blue">
8.<h1>リンクのページ</h1>
9.<ul>
10.<li><a href="link.cgi?url=html.cgi">例題1</a>
11.<li><a href="link.cgi?url=time.cgi">例題2</a>
12.<li><a href="link.cgi?url=dice.cgi">例題3</a>
13.<li><a href="link.cgi?url=env.cgi">例題4</a>
14.</ul>
15.</body>
16.</html>

スクリプト「link.cgi」
1.#!/usr/local/bin/perl
2.
3.$thisfile = 'link.cgi';
4.$datafile = 'link.dat';
5.$pass = '0123';#パスワード
6.
#---- 基本プログラム ----
7.&decode;
8.if ($in{'url'}) { &url; }
9.elsif (exists $in{'pw'}) { &admin; }
10.else { &main; }
11.exit;
12.
13.#---- フォームデータ取得 ----
14.sub decode {
15. if ($ENV{'REQUEST_METHOD'} eq 'POST') {
16. read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
17. } else {
18. $buf = $ENV{'QUERY_STRING'};
19. }
20. foreach (split(/&/, $buf)) {
21. ($key, $val) = split(/=/);
22. $val =~ tr/+/ /;
23. $val =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
24. $in{$key} = $val;
25. }
26.}
27.
28.#---- リンク先の計数と表示 ----
29.sub url {
30. $file = -e $datafile;
31. if ($file) {
32. open(IN, $datafile);
33. while (<IN>) {
34. chomp;
35. ($url, $count) = split(/,/);
36. if ($url eq $in{'url'}) { $count++; $flag = 1; }
37. push(@data, "$url,$count\n");
38. }
39. close(IN);
40. }
41. if ($flag != 1) { push(@data, "$in{'url'},1\n"); }
42. open(OUT, ">$datafile");
43. print OUT @data;
44. close(OUT);
45. if (!$file) { chmod(0666, $datafile); }
46. # リンク先を表示
47. print "location: $in{'url'}\n\n";
48.}
49.
50.#---- 管理画面への入り口 ----
51.sub main {
52. &html('この先は管理人しかは入れません');
53. print <<END;
54.<form action="$thisfile" method="post">
55.パスワード <input type="password" size="10" name="pw">
56.<input type="submit" value="管理画面へ">\n</form>
57.</body>\n</html>
58.END
59.}
60.
61.#---- 管理画面 ----
62.sub admin {
63. if (!$in{'pw'}) {
64. $s1 = 'パスワードが入力されていません';
65. } elsif ($in{'pw'} ne $pass) {
66. $s1 = 'パスワードが違います';
67. } elsif (! -e $datafile) {
68. $s1 = '閲覧記録はありません';
69. } elsif ($in{'del'}) {
70. unlink $datafile;
71. $s1 = '閲覧記録を削除しました';
72. }
73. if ($s1) {
74. &html($s1);
75. } else {
76. open(IN, $datafile);
77. @data = <IN>;
78. close(IN);
79. &html('閲覧記録');
80. print "<table border="1">\n";
81. print "<tr><th>URL</th><th>回数</th></tr>\n";
82. foreach (sort @data) {
83. chomp;
84. ($url, $count) = split(/,/);
85. print "<tr><td>$url</td><th>$count</th></tr>\n";
86. $total += $count;
87. print <<END;
88.<tr><th>Total</th><th>$total</th></tr>\</table>
89.<form action="$thisfile" method="post">
90.<input type="hidden" name="pw" value="$pass">
91.<input type="hidden" name="del" value="1">
92.<input type="submit" value="記録削除">\n</form>
93.END
94. }
95. }
96. print <<END;
97.<form action="$thisfile">
98.<input type="submit" value=" 戻る ">\n</form>
99.</body>\n</html>
100.END
101.}
102.
103.#---- HTML 書き出し ----
104.sub html {
105. print <<END;
106.Content-type: text/html\n
107.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
108.<html>\n<head>
109.<meta content="text/html;">
110.<title>閲覧記録管理</title>
111.</head>\n<body>
112.<h1>閲覧記録管理</h1>
113.<p><b>$_[0]</b></p>
114.END
115.}
●●● 解説 ●●●

1)スクリプト「link.html」
■ リンクデータ
先ず、link.html の中でどのようにリンクデータをlink.cgiへ送られるか?
→10~13行目の
  • 例題✗はlink.cgi を起動し、データとして url=html.cgi を渡している。また、データの渡し方を指定できない時は、デフォルトの GET でデータが渡される。更に、データのパス指定はlink.cgi からの 「相対パス」 で指定するか、url=http://localhost/samples/html.cgi と 「絶対パス」 で指定する。

    2)スクリプト「link.cgi」
    ■ リンクカウンタの動作原理
    1)link.html の中で、"link.cgi?url=html.cgi" としてリンクが張られ、link.cgi url=html.cgi というデータが送られてきたときの link.cgi での処理を見てみよう。

    基本プログラムの7行目の&decodeで、
    decodeサブルーチンを呼び出し、link.htmlから送られてきたデータをハッシュ %in に格納する。今回は、キー url の値 $in{'url'} が 'html.cgi' という文字列としてセットされる。

    基本プログラムの8行目のif ($in{'url'}){&url;}で、キー値が真に成り、urlサブルーチンが呼び出される。
    【urlサブルーチン】
    28.#---- リンク先の計数と表示 ----
    29.sub url {
    30. $file = -e $datafile;#ファイルテスト演算子
    31. if ($file) {
    32. open(IN, $datafile);
    33. while (<IN>) {
    34. chomp;
    35. ($url, $count) = split(/,/);
    36. if ($url eq $in{'url'}) { $count++; $flag = 1; }
    37. push(@data, "$url,$count\n");
    38. }
    39. close(IN);
    40. }
    41. if ($flag != 1) { push(@data, "$in{'url'},1\n"); }
    42. open(OUT, ">$datafile");
    43. print OUT @data;
    44. close(OUT);
    45. if (!$file) { chmod(0666, $datafile); }
    46. # リンク先を表示
    47. print "location: $in{'url'}\n\n";
    48.}
    urlサブルーチンの処理は、
    変数$datafile に代入した html.cgi へのリンク回数を 1 だけ加算して書き直すための処理。
    $datafile には、リンクされた順に 「リンク先」 と 「リンク回数」 をコンマで区切って、次のようにリンク先毎に1行に記録することに決めてあります。


    33~38行目の while ループでは
    $_ に各行が読み出されます。$_ の最後にある改行文字を chomp 関数で取り除き、split 関数 で $_ を区切り文字 「, 」 で分割して $url と $count に取り出しています

    37行目の push 関数 は、
    第1引数で指定した配列の末尾に、第2引数で指定した値を付け加える関数です。結局、この33~38行目の while ループでは、36行目でアクセス回数をインクレメントすることにより、ファイルの内容を書き換えるためのデータを配列 @data として準備したことになります。

    41行目の処理は、
    初めてのリンク先は $datafile の中に該当するデータが無いため、このリンク先をデータとして追加する処理です。

    42~44行目の処理は、
    @data を $datafile に書き込み、$datafile を新規に作成したときには、chmod 関数 で パーミッションを 666 に変更しています。

    47行目の print "location: $in{'url'}\n\n"; は、
    サーバーに対して 「この URL を表示してください」 と指示しています。この記述で改行記号が2つ付いていることに注意してください。CGI ヘッダーの出力と同様に2行目を空行にして出力しなければなりません。

    ■ 管理画面への入り口
    「link.cgi」を動作させると、基本プログラムより、「管理画面」 への入り口が表示されます。
    3.$thisfile = 'link.cgi';
    4.$datafile = 'link.dat';
    5.$pass = '0123';#パスワード
    #---- 基本プログラム ----
    7.&decode;
    8.if ($in{'url'}) { &url; }
    9.elsif (exists $in{'pw'}) { &admin; }
    10.else { &main; }
    11.exit;
    12.
    5行目に $pass として、管理者用のパスワードを設定しています。'Open Sesame' と設定しようと、'開けゴマ' と指定しようと、任意の文字列を設定します。

    →link.cgi が単純に起動されれば、url=xxx とか pw=xxx というようなデータは送られてきてはいないため、main サブルーチンが呼び出されます。
    50.#---- 管理画面への入り口 ----
    51.sub main {
    52. &html('この先は管理人しかは入れません');
    53. print <<END;
    54.<form action="$thisfile" method="post">
    55.パスワード <input type="password" size="10" name="pw">
    56.<input type="submit" value="管理画面へ">\n</form>
    57.</body>\n</html>
    58.END
    59.}

    52行目で html サブルーチンを呼び出し、'この先は管理人しかは入れません' というメッセージを表示する HTML の前半を出力し、ヒアドキュメントで 54~57行目のフォームを出力しています。このフォームで、パスワードの入力枠と 「管理画面へ」 という submit ボタンが作られます
    ■ 管理画面
    「管理画面」 を表示しているのは、この admin サブルーチンです。このサブルーチンは、9行目で (exists $in{'pw'}) が真の場合に呼び出されます。即ち、exists 関数を使って、ハッシュ %in の中に pw というキーが存在するかどうかを調べ、存在する場合にのみこのサブルーチンが呼び出されるようになっています。この管理画面では、「閲覧回数の集計」 を見たり、「閲覧記録をクリア」 することもできるようにしています
    61.#---- 管理画面 ----
    62.sub admin {
    63. if (!$in{'pw'}) {
    64. $s1 = 'パスワードが入力されていません';
    65. } elsif ($in{'pw'} ne $pass) {
    66. $s1 = 'パスワードが違います';
    67. } elsif (! -e $datafile) {
    68. $s1 = '閲覧記録はありません';
    69. } elsif ($in{'del'}) {
    70. unlink $datafile;
    71. $s1 = '閲覧記録を削除しました';
    72. }
    73. if ($s1) {
    74. &html($s1);←htmlサブルーチンの呼び出し
    75. } else {
    76. open(IN, $datafile);
    77. @data = <IN>;
    78. close(IN);
    79. &html('閲覧記録');←htmlサブルーチンの呼び出し
    80. print "<table border="1">\n";
    81. print "<tr><th>URL</th><th>回数</th></tr>\n";
    82. foreach (sort @data) {
    83. chomp;
    84. ($url, $count) = split(/,/);
    85. print "<tr><td>$url</td><th>$count</th></tr>\n";
    86. $total += $count;
    87. print <<END;
    88.<tr><th>Total</th><th>$total</th></tr>\</table>
    89.<form action="$thisfile" method="post">
    90.<input type="hidden" name="pw" value="$pass">
    91.<input type="hidden" name="del" value="1">
    92.<input type="submit" value="記録削除">\n</form>
    93.END
    94. }
    95. }
    96. print <<END;
    97.<form action="$thisfile">
    98.<input type="submit" value=" 戻る ">\n</form>
    99.</body>\n</html>
    100.END
    101.}
    63行目の (! $in{'pw'}) は、キー pw の値が空白なら真となる条件式です。空白は偽と見做されます。これに否定演算子 ! を付けて、「空白なら」 という条件式にしています。

    67行目でも否定演算子 ! を使っています。-e はファイルの存在を調べるファイルテスト演算子で、ファイルが存在すれば真を返します。これを否定して、「ファイルが無ければ」 という条件式にしています。

    67行目の条件式は、キー del の値が真ならば、という式です。「記録削除」 ボタンが押されると、del=1 というデータが送られるようにしてあるため、このデータを受けたときには unlink 関数で $datafile を削除してしまいます。

    63~72行目の何れかの条件が真の場合には、変数 $s1 にメッセージをセットします。従って、73行目の条件式が真となり、html サブルーチンで HTML の前半を出力して、96行目以降の処理を行って終了します。
    【htmlサブルーチン】
    103.#---- HTML 書き出し ----
    104.sub html {
    105. print <<END;
    106.Content-type: text/html\n
    107.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    108.<html>\n<head>
    109.<meta content="text/html;">
    110.<title>閲覧記録管理</title>
    111.</head>\n<body>
    112.<h1>閲覧記録管理</h1>
    #----引数表示----
    113.<p><b>$_[0]</b></p>
    114.END
    115.}


    上記以外の場合には、76行目以降の処理を行います。先ず、76~78行目でデータを @data に読み取り、html サブルーチンで HTML の前半を出力します。80~88行目はデータを表の形で出力しています。出力に際しては、URL をアルファベット順にソートして(82行目の sort 関数による)、合計回数を計算して(86行目)表の最後に書いています。

    89~92行目の出力で、「記録削除」 ボタンのあるフォームをを作っています。このフォームには、pw と del のコントロールを隠しておきます。

    96~100行目のヒアドキュメントによる出力は、「管理画面への入り口」 に戻すボタンを作っています。「戻す」 ボタンを押したときには、単純に $thisfile(link.cgi)にリンクを張ったときと同様の動作となり、次には main サブルーチンが呼び出されることになります。

  • コメント
    • Twitterでシェアする
    • Facebookでシェアする
    • はてなブックマークに追加する
    • LINEでシェアする

    アンケート2-(サブルーチン編)

    2018年04月08日 | perl
    アンケート2-(サブルーチン編)

    【環境条件】
    Eclipse 4.4(ルナ)
    XAMPP 1.8.3 
    Perlは既にインストール済み
    CGIを使用する時は、必ずApachを起動する

    ---アンケート2編---

    【操作画面】
    1)アンケートの質問画面

    2)回答画面

    3)アンケートの管理画面
    パスワードを入れないと表示されない。
    ここでは集計結果表示と集計リセットが出来る。


    【アンケート2】
    前回作ったシンプルなアンケートはenquete-1.html、enquete-2.cgi、enquete-3.cgi の3つのファイルを使っていましたが、今回のアンケート2では1つのファイル CGI で作成し直した。

    ■スクリプト「enquete.cgi 」
    1.#!/usr/local/bin/perl
    2.
    3.$thisfile = 'enquete.cgi';
    4.$datafile = 'enquete.dat';
    #パスワード設定
    5.$pass = '0123';
    6.@ans = ('犬', '猫', 'どちらも嫌い');#配列
    7.$ttl = '犬と猫のどちらが好き?';
    8.
    9.&decode;←サブルーチンを実行する命令
    10.if ($in{'mode'} eq 'kaitou') { &kaitou; }
    11.elsif ($in{'mode'} eq 'admin') { &admin; }
    12.else { &situmon; }
    13.exit;
    14.
    15.#---- フォームデータ取得 ----
    16.sub decode {
    17. if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    18. read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
    19. } else {
    20. $buf = $ENV{'QUERY_STRING'};
    21. }
    22. foreach (split(/&/, $buf)) {
    23. ($key, $val) = split(/=/);
    24. $in{$key} = $val;
    25. }
    26.}
    27.
    28.#---- 質問の表示 ----
    29.sub situmon {
    30. &html('質問', $ttl, '下から選んでご回答ください');
    31. print <<END;
    32.<form action="$thisfile" method="post">
    33.<input type="hidden" name="mode" value="kaitou">
    34.<ul>
    35.END
    36. for ($i=0; $i<@ans; $i++) {
    37. print qq(<li><input type="radio" name="ans" value="$i">$ans[$i]\n);
    38. }
    39. print <<END;
    40.</ul>
    41.<input type="submit" value="回答する">\n</form>
    42.<form action="$thisfile" method="post">
    43.<input type="hidden" name="mode" value="admin">
    44.パスワード <input type="password" size="10" name="pw">
    45.<input type="submit" value="管理画面">\n</form>
    46.</body>\n</html>
    47.END
    48.}
    49.
    50.#---- 回答の計数と表示 ----
    51.sub kaitou {
    52. if (exists $in{'ans'}) {
    53. @n = &readData($data);
    54. $i = $in{'ans'};
    55. $n[$i]++;
    56. open(OUT, ">$datafile");
    57. foreach (@n) { print OUT "$_\n"; }
    58. close(OUT);
    59. if (!$data) { chmod(0666, $datafile); }
    60. $msg = "あなたは 「$ans[$i]」 の$n[$i]人目です";
    61. } else {
    62. $msg = "どれかを選んでからお答えください";
    63. }
    64. &html('回答', 'ご回答の表\示', $msg);
    65. &back;
    66.}
    67.
    68.#---- 管理画面 ----
    69.sub admin {
    70. if (!$in{'pw'}) {
    71. $msg = 'パスワードが入力されていません'; $flag=0;
    72. } elsif ($in{'pw'} ne $pass) {
    73. $msg = 'パスワードが違います'; $flag=0;
    74. } elsif ($in{'del'}) {
    75. unlink $datafile;
    76. $msg = '集計ファイルを削除しました'; $flag=1;
    77. } else {
    78. $msg = '集計結果'; $flag=1;
    79. }
    80. &html('集計', '集計結果', $msg);
    81. if ($flag == 1) {
    82. @n = &readData($data);
    83. print "<ul>\n";
    84. $i = 0;
    85. foreach (@ans) { print "<li>$_ : $n[$i++]\n"; }
    86. print "</ul>\n";
    87. if ($data) { print <
    ●●● 解説 ●●●

    ■サブルーチンについて
    ・サブルーチン定義
    sub サブルーチン名 { ・・・ }

    ・サブルーチンの呼び出し
    &サブルーチン名;

    ・サブルーチンに引数を渡すには
    &サブルーチン名( 引数1, 引数2, ・・・ 引数n );
    但し、引数は 「@_ 」 という名前の配列でサブルーチンに渡されます。配列 @_ の添え字は 0 から始まり、最後の添え字は 「$#_ 」 という変数に格納されます。従って、サブルーチンの中では $_[0]、$_[1]、 ・・・ $_[$#_] で、各引数の値を参照したり、逆に各引数の値を設定したりすることができます。また サブルーチンからの戻り値は、サブルーチンの中で return 関数を使って返す
    Perlのサブルーチン
    例文
    # (1)引数に渡す値
    my $num1 = 1;
    my $num2 = 2;

    # (2)サブルーチンの呼び出し
    my $total = total($num1, $num2);値は3になる

    # (3)サブルーチンの定義
    sub total {
    my ($num1, $num2) = @_;
    my $total = $num1 + $num2;
    return $total;
    }
    戻り値は$totalに代入される。

    ●●●解説●●●

    1)「基本プログラム」

    9.&decode;←ルーチンの呼び出し
    10.if ($in{'mode'} eq 'kaitou') { &kaitou; }←ルーチンkaitouの呼び出し
    11.elsif ($in{'mode'} eq 'admin') { &admin; }←ルーチンadminの呼び出し
    12.else { &situmon; }←ルーチンsitumonの呼び出し
    13.exit;
    14.

    9~14行に書かれたプログラムを「基本プログラム」と呼びます。この「基本プログラム」は、いろいろなサブルーチンを呼び出してプログラム全体を進行させ、13行目の exit; でプログラムは終了する。また、いろいろな処理をサブルーチン化しているため、非常に簡潔になっている。

    ★サブルーチンdecode

    #---- フォームデータ取得 ----
    16.sub decode {
    17. if ($ENV{'REQUEST_METHOD'} eq 'POST') {
    #read関数は環境変数 'CONTENT_LENGTH' にセットされた長さの文字列を、STDIN から 変数 $buf に読み込む。
    18. read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
    19. } else {
    20. $buf = $ENV{'QUERY_STRING'};
    21. }
    22. foreach (split(/&/, $buf)) {
    23. ($key, $val) = split(/=/);
    24. $in{$key} = $val;
    25. }
    26.}
    例題の「フォームデータの取得」 の部分をサブルーチンとして纏めたもの。最初に、このサブルーチンを実行し、他のサブルーチンからデータが送られて来た時、そのデータをハッシュ %in に格納する処理をしている。

    #---- 質問の表示 ----
    sub situmon {
     ・・・
    }
    #---- 回答の集計と表示 ----
    sub kaitou {
     ・・・
    }

    #---- 管理画面 ----
    sub admin {
     ・・・
    }

    始めはデータ送信されない為、ハッシュ%inは作られない。よって、パスされる。次の10~12行目では、$in{'mode'}=kaitouの場合は、サブルーチンkaitouを呼び出し、$in{'mode'}=adminの場合はサブルーチンadminを呼び出す。それ以外はサブルーチンsitumonを呼び出して、次のexit命令で終了する。

    ■アンケートの質問画面
    サブルーチンsitumonのスクリプト
    #---- 質問の表示 ----
    29.sub situmon {

    サブルーチンhtmlの呼び出し
    30. &html('質問', $ttl, '下から選んでご回答ください');

    31. print <<END;←ドキュメント
    32.<form action="$thisfile" method="post">
    33.<input type="hidden" name="mode" value="kaitou">←隠しキー
    34.<ul>
    35.END
    36. for ($i=0; $i<@ans; $i++) {
    37. print qq(<li><input type="radio" name="ans" value="$i">$ans[$i]\n);
    38. }
    39. print </ul>
    41.<input type="submit" value="回答する">\n</form>
    42.<form action="$thisfile" method="post">
    43.<input type="hidden" name="mode" value="admin">
    44.パスワード<input type="password" size="10" name="pw">←パスワード入力欄が作成される
    45.<input type="submit" value="管理画面">\n</form>
    46.</body>\n</html>
    47.END
    48.}
    →→→サブルーチンsitumonの30行で、サブルーチンhtmlを引数を付けて呼び出し、HTMLの前半部分を出力し、31行目以降で2つのフォームを含む HTML の後半を出力している。
    32行目の<form action="$thisfile" method="post">で、自分自身を呼び出すように設定している。
    36~37行目の for ループで質問に対する回答項目を出力しています。ループ条件を $i<@ans として $ans[$i] を出力するようにしていますが、これは、配列 @ans の内容、要素数を変更しても、ここのスクリプトを変更しないで済むようにしているためです。

    以上、実行すると
    「犬と猫のどちらが好き?」と言う質問画面を表示される。

    ★サブルーチンhtml
    #---- HTML 書き出し ----
    117.sub html {
    118.print <<END;←ヒアドキュメント
    119.Content-type: text/html\n
    120.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    121.<html>\n<head>
    122.<meta content="text/html;">
    123.<title>アンケートの$_[0]</title>
    124.</head>\n
      <body>
    125.<h1>$_[1]</h1>
    126.<p><b>$_[2]</b></p>
    127.END
    128.}
    →→→ このサブルーチンsitumonは、CGIヘッダーの2行と HTML の head 部 および body 部の途中までを出力している。また、渡される3つの引数を($_[0]→質問、$_[1]→犬と猫のどちらが好き?、$_[2]→下から選んでご回答ください)で参照して、それらの文字列を 123, 125, 126行目で使用している。

    ■ 回答画面
    このサブルーチンは、回答された数を計数してから、回答内容を表示する画面を出力します。また、このサブルーチン内で、「readData サブルーチン」、「html サブルーチン」、「back サブルーチン」 の3つのサブルーチンを呼び出してる。

    サブルーチンkaitouのスクリプト
    #---- 回答の計数と表示 ----
    51.sub kaitou {
    52. if (exists $in{'ans'}) {
    53. @n = &readData($data);
    54. $i = $in{'ans'};
    55. $n[$i]++;
    56. open(OUT, ">$datafile");#書き込みモード
    57. foreach (@n) { print OUT "$_\n"; }
    58. close(OUT);
    59. if (!$data) { chmod(0666, $datafile); }
    60. $msg = "あなたは 「$ans[$i]」 の$n[$i]人目です";
    61. } else {
    62. $msg = "どれかを選んでからお答えください";
    63. }
    64. <font color="red">&html('回答', 'ご回答の表\示', $msg); </font>
    65. &back;
    66.}
    →→→53行目の@n = &readData($data); で、データファイルを読み取り、回答数を@n で受け取っています。引数 $data には、データファイルが存在すれば真、存在しなければ偽がセットされます。

    ★サブルーチンreadData
    99.#---- 集計データ読み取り ----
    100.sub readData {
    101. my @num; #myはローカルでのみ有効
    102. $_[0] = -e $datafile;
    103. if ($_[0]) {
    104. open(IN, $datafile); #読み込みモード
    105. while (<IN>) {
    106. chomp;
    107. push(@num, $_); #配列にデータを代入
    108. }
    109. close(IN);
    110. } else {
    111. for (my $i=0; $i<@ans; $i++) { $num[$i] = 0; }
    112. }
    113. return @num;
    114.}
    →→→102行目でファイルテスト演算子 -e を使って $datafile が存在するかどうかを調べる。データファイルが存在すれば真を、存在しなければ偽をセットする。
    ・ファイルが存在した場合
    このファイルの内容を読み取り、これまでのデータを配列numに格納します
    ・データファイルが存在しない場合
    111行目の for ループで @ans と同じ要素数をもつ @num の各要素を0に設定して、これを戻り値とする。
    ★サブルーチンback
    130.#---- 戻るボタン ----
    131.sub back {
    132. print <<END;
    133.<form action="$thisfile">
    134.<input type="submit" value=" 戻る ">\n</form>
    135.</body>\n</html>
    136.END
    137.}

    ■ 管理画面
    管理画面は、前回の「集計結果の表示画面」 に類似の画面ですが、管理者だけがアンケートの集計を見ることができ、必要なときには集計をリセットすることができるようにした画面です。集計をリセットする機能は前回にはありませんでしたが、この例題で追加しました。「集計リセット」 というボタンを設け、これが押されたときにデータファイルを削除してしまいます。

    #---- 管理画面 ----
    69.sub admin {
    70. if (!$in{'pw'}) {
    71. $msg = 'パスワードが入力されていません'; $flag=0;
    72. } elsif ($in{'pw'} ne $pass) {
    73. $msg = 'パスワードが違います'; $flag=0;
    74. } elsif ($in{'del'}) {
    75. unlink $datafile; #ファイルの削除
    76. $msg = '集計ファイルを削除しました'; $flag=1;
    77. } else {
    78. $msg = '集計結果'; $flag=1;
    79. }
    80. &html('集計', '集計結果', $msg);

    81. if ($flag == 1) {
    82. @n = &readData($data);
    83. print "<ul>\n";
    84. $i = 0;
    85. foreach (@ans) { print "<li>$_ : $n[$i++]\n"; }
    86. print "</ul>\n";
    87. if ($data) { print <

    93.END
    94. }
    95. }
    96. &back;#サブルーチンbackの呼び出し
    97.}
    →→→70~79行目で、この admin サブルーチンがどのような状態で呼び出されたのかを調べています。パスワードが正しく入力されていなければ、何らかのメッセージを表示して 「戻る」 ボタンで最初の画面に戻ってもらいます。この状態を $flag=0; として82~94行目の処理がスキップされる。

    →→→「集計リセット」 ボタンが押された直後でなければ、78行目が行われた後に82~94行目の処理に進みます。82行目でデータが読み出され、83~86行目の処理でデータを表示します。若しデータファイルが存在すれば、ヒアドキュメントで88~92行目で 「集計リセット」 ボタンのあるフォームを出力します。

    このフォームには隠しコントロールを3つ設け、「集計リセット」ボタンが押されたときに enquete.cgi が mode=admin、pw=$pass、del=1 というデータを伴って起動されるようにしておきます。従って、「集計リセット」 ボタンが押された直後には、再びこの admin サブルーチンが呼び出されて75行目にある unlink $datafile; が実行されます。

    以上









    コメント
    • Twitterでシェアする
    • Facebookでシェアする
    • はてなブックマークに追加する
    • LINEでシェアする