Debug- Official Website -


犬土偶日記

海の近くに住みたい

Debug日記の使い方

話題:Webプログラミングとか

2006年10月16日

はじめてのPHP

公開日時: 不明

Webサイトを作つたことがある若しくは運営してゐるといふ人なら大体知つてゐると思ふが、クライアント(ウェブブラウザ等)の要求に応じて動的にページを送出するCGIといふものがある。URLの拡張子が「.cgi」になつてるやつだ。主に掲示板やチャット等がそれに該当する。CGIといふのは、クライアントがサーバ上のプログラムを起動するための規格のことで、サーバサイドで動くプログラム自体のことを言ふわけではないのだが、あまり厳密に定義に従はずに用語を使つても特に不都合が無いからか、CGIと言へばサーバで動くプログラムのことだ、といふやうな感じになつてゐる。主にPerlといふ言語でプログラムされる。RubyやC言語も使はれるが、昔から圧倒的にPerlが使はれてゐる。Webでは最終的にHTMLを画面に書き出すことが多い。HTMLといふのはプログラムに文書の構造を認識させるタグといふものを付けただけの単なるテキストなので、テキスト処理に強いPerlが使はれるのは自然なことだと言へる。大体CGIといふものにはパターンがあつて、大雑把に言へば、入力→処理→出力の3段階。掲示板にしろチャットにしろアクセスカウンターにしろ、大体同じ仕組みだ。入力といふのは訪問者のアクセス(プログラム起動)やフォームデータの送信等。処理は、クライアントから送られたデータを元にあんなことやこんなことを好き勝手にやりたい放題。で、ファイルや画面に書き出すといふ出力でフィニッシュ。掲示板では、まづ誰かがbbs.cgi等にアクセスすると、サーバ上に保存された書き込みデータをプログラムが読み出し、それを元に実際にブラウザで表示される画面を書き出す。ここで一旦終了。訪問者のアクセス(入力)→データ読み出し&表示用に整形等(処理)→画面に書き出す(出力)といふ感じ。で、フォームに名前や文章を入力して送信ボタンを押すと、フォームデータがサーバに送信され(入力)、そのデータから危険な文字列を消したり色々チェックしたりして(処理)、それをデータファイルに保存(出力)、そしてそのデータを元にブラウザにも出力。CGIは最後に必ず標準出力(この場合はウェブブラウザへの送出)が必要になる。ファイルに書き出すだけで終らうとするとエラーが出る。必ず画面に何らかの結果を表示しないといけない。まあそれはいい。とにかく、基本的にはどんなものでも大体同じやうなことをしてゐる。チャットは掲示板と全く同じものだし(2ちゃんねるの実況のやうに流れの速い掲示板とチャットの区別はほとんど無いだらう)、Web日記も掲示板と同じものだし(特定の人間だけが書き込める掲示板)、ブログも全く同じものだ。アクセス解析CGIは訪問者には変化が分からないので特殊な感じもするだらうが、実は同じやうなものだ。アクセスした時点で環境変数といふものを色々と勝手にブラウザが送つてゐる。といふか、送らなければサーバ側はどこにデータを送り返していいのか判断できない。非通知で赤の他人に電話かけて自分の番号も教へずに一旦切るので折り返し掛けてくださいなんて言つても絶対に掛かつて来ないのと同じだ。その送られた環境変数といふデータ(入力)を、都合の良いやうに色々料理して(処理)、ファイルに記録し(出力)、その上で普段と変はらない通常のページを表示する。特別なことはしてゐない。とにかくCGIといふのは、お決まりのパターンで同じやうなことしかしてゐないのだ。

長々とCGIの説明をしてきたが、DebugのサイトにもCGIは多く使はれてゐる。むしろ通常ページよりもCGIコンテンツの方が多い。一般訪問者から見ると、掲示板と日記とスケジュールしか無いやうに見えるが、実はプロフィールのページもCGIプログラムによつて更新されてゐる。スケジュールも普通のHTMLを見てるのと大差ない感じだらう。プロフィールは犬土偶が自分のPC上でテキストエディタに手打ちしてアップロードしてゐるのではなく、サーバ上にあるプロフィール更新CGIを各メンバーが起動して自分で編集してゐるのだ。これも掲示板と同じ仕組み。メンバーページは全てCGIになつてゐる。パスワードを変更するとか、スケジュールのページを更新するとか、日記を書くとか、プロフィールを更新するとか、まあ他にも色々あるんだが、一般訪問者の見えないところにCGIが多く使はれてゐる。別にアクセス者の個人情報を集めようとか、さういふことはしてゐないので安心しろ。サイトの更新の手間を軽減するのが目的だ。各メンバーが独自にやりたいこともあるだらう。日記なんか手動更新だとキツいぞ。例へば椅子タンが日記を更新しようと思つた場合、手動でしか更新できなかつたらまづはメモ帳などのテキストエディタに椅子タンが日記を書き、それを俺にメールで送り、俺がメールを受け取つてからその文章を元にHTMLファイルを作り(これが鬼のやうに大変)、それをFTPでサーバにアップロードだ。無理。更新も時間が掛かる。CGIなら楽だし、執筆者本人がいつでも手直ししたい時に好きなやうにできる。いつ見ても全く変化のない通常のHTMLだけのサイトと比べてもCGIコンテンツがある方が面白味があるサイトになると思ふ。まあ使ひ方次第だが。何にしてもCGIを使ふには当然プログラミングの知識が必須になる。誰かが作つたプログラムを使はせてもらふにしても、そのまま使ふだけではダメだらう。多少の改造等が必要になる。で、CGIをやるならPerlを勉強せねばならん。俺は2000年春に自分のサイトをやり始めた頃からいきなり掲示板の改造等でCGIを弄つてたので、遊びながらいつの間にか自然にできるやうになつてゐた。と言つても6冊ぐらゐは専門書を読んだし、ネットでも色々調べたし、実際のプログラミングもKB単位ではなくMB単位で書いたし、習得にはそれなりのことをしてゐる。DebugのサイトのCGIは全て自分で作つた。BRの方では通常掲示板やメンバー用掲示板で他所で配布してるフリーのスクリプトを使つてゐるが、Debugは全て自作。自分で作れるやうになるとなかなか面白い。ただテキストエディタやHTMLエディタでページを作成してFTPでアップロードするだけといふのよりも遥かに面白い。アイデア次第で色々できるしな。フォームを用意して色々とユーザに入力させ、そのデータを使つてMIDIファイルを作るといふやうなこともできる(MIDIの仕組みを知らないとダメだけど)。例へばそのMIDI作成CGIと投票CGIみたいなのをセットにすれば作曲コンテストなんてのもサイト上でできることになる。まあプログラムするのは大変だけどな。

少し話は変はるが、Webでのプログラムには大別して2種類ある。サーバサイドで実行されるものと、クライアントサイドで実行されるものだ。今まで長々と前者について語つてきたが、後者についても少し言及しておかう。後者は主にJavaScriptが使はれ、Webブラウザ上で実行される。動的であるかのやうなページにできるが、サーバ上でファイルの書き換へ等ができないので、そのユーザーのみで完結する。例へば、JavaScriptで掲示板のやうなものも作ることはできる。フォームを用意して、そのフォームに文章を入力させ、ボタンを押すとJavaScriptが起動して画面上の文章を書き換へる。見た目は通常の掲示板と同じに見えるが、変更をファイルに保存できない、といふか、自分のブラウザの中だけで起こつてゐることなので他のユーザーからはその書き込みを見ることができない。ブラウザを終了させたらそのデータは消えてしまふ。つまりいつ見ても同じといふ点でただのHTMLと何ら変はりはない。CGIならサーバ上のファイルを書き換へるので、ロシアから見てもアメリカから見ても、Aさんが見てもBさんが見ても他人の書き込みを確認できる。JavaScriptはあくまでも自分だけ。だから、JavaScriptではブラウザの画面で何かがチラチラと無駄に動くとか色が変化するとか画面サイズが変更されるとか、さういふウザいだけのことしかできない。サーバで動くプログラムの方が格段に面白いことができる。Webサイトを運営してゐる人はサーバサイドプログラミングに挑戦した方がより楽しめると思ふ。

CGIと言へばPerl。CGIが使へるといふサーバでもPerlのCGIのみを想定してたりする。最近はさうでもないみたいが、とにかく圧倒的にCGIと言へばPerlといふ感じだ。だから俺は今までPerlだけでやつてきた。でも今のレンタルサーバではPHPも使へる。PHPといふのは、HTML内にスクリプトを書くタイプのプログラム。さう聞くとクライアントサイドで動くJavaScriptみたいなものかと思つてしまふが、実はサーバサイドで動くのだ。まあSSIみたいなものだ。どうもPHPはWebで使ひやすいやうな仕様になつてゐて、標準で色々と便利な機能があるらしい。さらに実行速度もPerlのCGIより速いとか。最近PHPのコンテンツが増えてきた。アップローダ等でもCGIよりPHPが主流になりつつある。掲示板でもPHPのものが少しづつ増えてきた。恐らく今後はPHPの方が主流になるのではないかと思ふ。データベースとの連携も強いし。まあそんなわけで、折角今のサーバでPHPが使へるので、PHPを習得してみようと思ふわけです。今までの長い長い文章はただの前振りだつたのですよ。ここからがメイン。

今PHPの解説書を読んでる途中なのだが、見た感じ、PerlやC言語と似た構文のスクリプトだ。プログラミングといふものは、1つでも習得すれば結構応用が利くものだ。似た系統のものならなおさらだ。変数の型、演算子、制御文、サブルーチン、入出力が分かれば後は関数リファレンスを見ながら適当に書けたりする。それぞれの言語に特有の定石みたいな書き方があつたりもするんだが、知らなくても何とかなる。効率の良い書き方を習得するためには他人が書いたプログラムを色々読んだりしないといけないんだが、取り敢へず今は基本的なところからといふ感じ。プログラミングは本を読むだけでは絶対に身につかない。必ず実際にプログラムする必要がある。で、まづは本を読んでからと思つてたんだが、結構Perlに似てるといふこともあつて、読みながら既に飽き気味になつてゐる。まだ全然知識が足りない状態だが、ここらで軽くプログラミングしてみるのもいいかもしれないと思つてみたりした。Webで使ふサーバサイドのプログラムはお決まりのパターンばかりだと書いた。基本は掲示板。ゲストブックとか、さういふシンプルなものが最小の基本形だと思ふ。フォームデータを受け取つて記録するだけのもの。それを画面に表示するだけ。非常にシンプルだが掲示板の根幹部分。これを作つてみようと思つた。CGIだつたら10〜15分もあれば作れる。まづはCGI版を作つて、それと全く同じものをPHPでも作つてみることにした。無限にデータを記録するとサーバ容量とか大変だし、量が増えると表示が重くなつたり大変なので、15件のみ記録することにした。暇だつたら何か書き込んでみろ。書き込みがページに反映するのが分かる。典型的なCGIの掲示板の基本形。

適当BBS Perl CGI版 http://orca.xii.jp/test/testbbs-perl.cgi

#!/usr/local/bin/perl

require "../debug/lib/cgi-lib.pl";

&ReadParse;

open (LOG, "./log.txt");
@log = <LOG>;
close (LOG);

if($in{'name'} && $in{'message'}){

    $in{'name'} =~ s/\x0D\x0A//g;
    $in{'name'} =~ s/\x0D//g;
    $in{'name'} =~ s/\x0A//g;
    $in{'name'} =~ s/\t/\x20/g;
    $in{'name'} =~ s/&/&amp;/g;
    $in{'name'} =~ s/</&lt;/g;
    $in{'name'} =~ s/>/&gt;/g;
    $in{'name'} =~ s/"/&quot;/g;
    $in{'name'} =~ s/'/&#039;/g;
    $in{'message'} =~ s/\x0D\x0A//g;
    $in{'message'} =~ s/\x0D//g;
    $in{'message'} =~ s/\x0A//g;
    $in{'message'} =~ s/\t/\x20/g;
    $in{'message'} =~ s/&/&amp;/g;
    $in{'message'} =~ s/</&lt;/g;
    $in{'message'} =~ s/>/&gt;/g;
    $in{'message'} =~ s/"/&quot;/g;
    $in{'message'} =~ s/'/&#039;/g;

    unshift (@log, "$in{'name'}\t$in{'message'}\n");

    if(@log > 15){
        pop (@log);
    }

    open (LOG, ">./log.txt");
    eval "flock (LOG, 2);";
    print LOG @log;
    eval "flock (LOG,8);";
    close (LOG);

}

print <<"HTML";
Content-Type: text/html; charset=EUC-JP

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
 <head>
  <meta http-equiv="content-type" content="text/html; charset=EUC-JP">
  <meta http-equiv="content-style-type" content="text/css">
  <link rel="stylesheet" href="../debug/debug.css" type="text/css">
  <title>適当BBS Perl CGI版</title>
 </head>
 <body>
  <h1>適当BBS</h1>
  <div class="content">
   <h2>Perl CGI版</h2>
   <p>言いたいことがあるなら言ってみろ。</p>
HTML

if(@log){
    print"   <ul>\n";

    chomp (@log);
    foreach (@log){
        ($name, $message) = split (/\t/, $_);
        print "    <li>$name &gt;&gt; $message</li>\n";
    }

    print "   </ul>\n";
}else{
    print "   <p>まだ何も書かれてません。</p>\n";
}

print <<"HTML";
   <form action="./testbbs-perl.cgi" method="post">
    <fieldset>
     <legend>メッセージ投稿フォーム</legend>
     <p>名前:<input type="text" name="name" size="20"></p>
     <p>コメント:<input type="text" name="message" size="70"></p>
     <p><input type="submit" value=" 送信 "></p>
    </fieldset>
   </form>
  </div>
 </body>
</html>
HTML

exit;

フォームデータの受信はcgi-lib.plといふライブラリのReadParseといふサブルーチンで行つてゐる。&ReadParse;といふ部分でそのサブルーチンを呼び出すと、%inといふハッシュに自動的に格納される。フォームデータが存在すればフォームからの送信であると判断できる。無ければただ表示を要求されただけと判断できる。if文でそれを条件に処理を変へる。フォームデータがあればまづは都合の悪い文字列を安全なものに置換。最終的にHTMLで画面に出力するので、フォームデータにHTMLタグが紛れ込むと厄介なことになつたりする。ユーザーの入力をチェックするのはセキュリティの基本。ここでは大したことはしてゐない。(本当は入力をそのまま記録し、表示時にサニタイズするのが基本。ここでは先に置換してから書き込むのでデータの再利用時に不都合が生じる可能性を持つてゐる。)文字列置換後にフォームデータをログファイルに書き込む。ログファイルに書き込んだ後、若しくはフォームデータが無い場合はログデータを読み込んでそれを元に画面にページを書き出す。これだけ。非常にシンプルだがこれで動く。見て分かるやうに、最も大変なのがHTMLを書き出す部分だ。Perlのプログラムとはほとんど関係がない。ただHTMLを書くのが非常に大変なのだ。面倒臭いにも程がある。こんなシンプルなやつならまだ良いが、もしこれがTABLEレイアウトで何層にも入れ子になつた複雑なページだつたらどうしよう。ただコピペすれば良いといふなら楽だが、データファイルを読み込んでその結果をページに反映させねばならんので、そのHTMLの然るべき場所に変数を書き込まねばならない。上記CGIでは$name$messageをforeach文で繰り返し書き出してゐる部分だ。どこに何を表示するのかといふことが一瞬で分かるやうなシンプルなページならばプログラムに組み込むのは容易い。しかし無駄に複雑なページだと大変だ。しかも他人が用意したHTMLだつたりすると気が狂ふ程だ。まづはHTMLの解読からやらないといけないからな。どこに何があるのかわからない、どこに何を表示すべきか探すのが面倒臭い、さういふのは嫌だ。とにかく、かういふCGIで大変なのはHTMLを書き出す部分だ。難しくはない。誰でもできる。でも激しく面倒臭い。まあこの程度のCGIなら10分程度でできる。休まず書き殴る感じであつと言ふ間に完成だ。HTML部分が一番時間掛かつた。これはPHP版を作つて比較するためだけのものだからどうでもいい。続いてドッキドキのPHP版。生まれて初めてのPHPプログラミング。果たして全く同じものが作れるのか。

適当BBS PHP版 http://orca.xii.jp/test/testbbs-php.php

<?php

$log = file ("./log.txt");

$name = $_POST['name'];
$message = $_POST['message'];

if($name && $message){

    $name = str_replace ("\x0D\x0A", "", $name);
    $name = str_replace ("\x0D", "", $name);
    $name = str_replace ("\x0A", "", $name);
    $name = str_replace ("\t", "\x20", $name);
    $name = htmlspecialchars ($name);
    $message = str_replace ("\x0D\x0A", "", $message);
    $message = str_replace ("\x0D", "", $message);
    $message = str_replace ("\x0A", "", $message);
    $message = str_replace ("\t", "\x20", $message);
    $message = htmlspecialchars ($message);

    array_unshift ($log, "$name\t$message\n");

    if(count($log) > 15){
        array_pop ($log);
    }

    $fp = fopen("./log.txt", "w");
    flock ($fp, LOCK_EX);
    fwrite ($fp, join("", $log));
    flock ($fp, LOCK_UN);
    fclose ($fp);
}

header ("Content-Type: text/html; charset=EUC-JP");

?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
 <head>
  <meta http-equiv="content-type" content="text/html; charset=EUC-JP">
  <meta http-equiv="content-style-type" content="text/css">
  <link rel="stylesheet" href="../debug/debug.css" type="text/css">
  <title>適当BBS PHP版</title>
 </head>
 <body>
  <h1>適当BBS</h1>
  <div class="content">
   <h2>PHP版</h2>
   <p>言いたいことがあるなら言ってみろ。</p>
<?php

if($log){
    echo "   <ul>\n";

    foreach ($log as $line){
        $line = rtrim ($line, "\n");
        list ($log_name, $log_message) = split ("\t", $line);

        echo "    <li>$log_name &gt;&gt; $log_message</li>\n";
    }

    echo "   </ul>\n";
}else{
    echo "   <p>まだ何も書かれてません。</p>\n";
}

?>
   <form action="./testbbs-php.php" method="post">
    <fieldset>
     <legend>メッセージ投稿フォーム</legend>
     <p>名前:<input type="text" name="name" size="20"></p>
     <p>コメント:<input type="text" name="message" size="70"></p>
     <p><input type="submit" value=" 送信 "></p>
    </fieldset>
   </form>
  </div>
 </body>
</html>

一応PerlのCGIと全く同じことをしようといふつもりで書いてみたのだが、まづPHPでは自動でフォームデータを受け取ることができて非常に便利。GETメソッドで送られたデータが$_GETに自動的に格納され、POSTメソッドで送られたデータは$_POSTに格納される。しかもデコードされた状態で。これは便利すぎる。cgi-lib::ReadParseを使ふやうな面倒なことをする必要が無い。ただ、ファイルに書き込むところでPerl版と違ふことをしてゐる。fwrite ($fp, join("", $log));の部分だ。Perlではファイルに配列をそのまま書き込めたんだが、PHPではできなかつた。Perlは配列をスカラーコンテキストで評価すると配列の要素数を返す。PHPでは文字列「Array」を返す。だから書き込み件数が15件を越えてるかどうかの判断でPerl版では@logを15と直接比較してゐるし、PHPでは配列の要素数を返すcountといふ関数を使つてゐる。で、ファイルの書き込みではPHPは強制的に配列を文字列として扱ふやうで、ログファイルに「Array」と記録されてしまつた。これではダメだ。といふわけで、配列の要素を第1引数の文字で連結するといふ関数joinを使ひ、配列を1つの文字列にしてファイルに書き込むことにした。これは面倒だ。配列を直接ファイルに書き出す方法もあるのかもしれないが、今の俺は知らない。ここは取り敢へず妥協。Perlの方が柔軟で融通が利く感じ。PHPはHTMLファイルの中に記述するので、HTMLを画面に出力したい時はそのままHTMLを書けばいい。これはかなり楽だ。上のPerl版ではヒアドキュメントを使つてるが、それすらも必要ない。まあプログラムを見比べるとほとんど何も変はらない感じなんだが、慣れもあるかもしれないが、感触としてはPerlの方がやりやすい印象がある。しかしやはりPHPはWeb用に使ふことを意識して作られてる感じがすこぶる良い感じ。どちらが総合的に効率が良いのかこれだけでは分からん。といふか、PHP版はもつと違ふ書き方をするのが普通なのかもしれない。他人の書いたPHPスクリプトを見たことがないので、どういふ感じで書けばいいのかが分かつてゐない。PerlのCGIと同じ感じでやつてみただけ。実を言ふと、このPHP版を書くのに1時間掛かつた。死ね俺。Perl版の何倍掛かつてんだよ。パースエラー出まくるし。でもまあ、何とか動いたので良しとしよう。PHPはあらゆる種類の変数に$を付けるのがわかりにくい。Perlみたいにスカラーが$で配列が@でハッシュが%といふ風に分けられてた方がいいな。さういふ仕組みになつてればfwrite($fp, @log);みたいにファイルに配列を直接書けるんぢやねえの?別のやり方でできるのかもしれんけど。まあ多少の違ひは慣れればどうにでもなるだらう。取り敢へず動くPHPを書けたので第1段階はクリアだな。まあ一般的にはまづ<?php echo "Hello!World" ?>なんだらうが、それでは何の意味もない。

あまりにもシンプル過ぎて掲示板といふ感じではないが、これに色々と機能を追加すると普通の掲示板になる。レスを付ける機能、書き込みを削除する機能、文字色を選べる機能、アクセスログを取る機能、アクセス数を記録する機能、データ量が増えたらページを分けて表示する機能、荒らしに対するアクセス制限機能、書き込み順にソートして表示する機能など。ありがちな掲示板が思ひ浮かぶだらう。今回のサンプルから機能追加の方向性を変へれば日記にもなるしチャットにもなるしアンケートにもなる。これが基本の最小単位だと思ふ。これができたならもつと複雑なのもできるといふことだ。これからはPHPも使つて行かうと思ふ。まあまだ知識が足りなさ過ぎるから、取り敢へず読みかけの本を読み切つて、他人の書いたスクリプトを眺めたりせねばなるまい。1年後に今日のスクリプト見たら「何だこれ!!ウンコぢやねえか!!」つて感じなんだらうな。PHP使へる人居る?何かをかしい部分があつたら教へてくれ。今日の日記はこれで終了。まだ他にネタはあるのだが、無意味に出し惜しみ。出し惜しんだまま忘れ去つてお蔵入りする可能性もあるがどうでもいい。お前はウンコ食つて苦しんで死ねば良いよ。

話題:Webプログラミングとか

Info.
公開日時不明
本文文字数12835文字 (タグ込み)
URLhttps://orca.xii.jp/debug/diary/diary.cgi?id=dogoo;date=20061016
RSS1.0https://orca.xii.jp/info/diary-dogoo.rdf
Comments

コメントはありません。

コメント投稿フォーム
文字色              
  • 名前を省略すると「名無しDebugger」になります。
  • メールアドレスの入力は任意です。
  • 海外からのコメントスパム対策のため、表示された漢字の読みを必ず入力してください。
  • 本文は必須項目です。投稿する場合は必ず記入してください。タグは使えません。
  • 改行が1つ入力された場合は強制改行として処理されますが、2つ以上連続する改行は段落の終了として処理されます。
  • 本文の行頭に「>」のある文は引用としてマークします。引用でない部分の冒頭に「>」は付けないでください。
  • コメントの削除は管理者若しくは日記執筆者しかできません。書き込む時は注意しましょう。
  • 全部記入が済んだら投稿ボタンを押す前に一度読み直して推敲しましょう。