HTMLにはセッション機能がありません。
セッションとは、簡単に言うとリクエスト間の繋がりです。
ある時ページAが呼び出され、次にページBが呼び出されたとして、そのAとBが同一人物から呼び出されたのか、それとも別人から呼び出されたのかがわかりません。
また、ページAからページBに情報を渡すといったこともできません。
要するにログイン認証のあるページが作れず、ショッピングカートも作れないということです。
それではあんまりなので、多くのサイトではPerlやPHP等の言語を用いてセッションを実装しています。
以下は、よく使われているセッションの仕組みです。
まずサーバ側で適当な文字列を用意し、クライアントのクッキーにセットします(セッションIDと呼ぶ)
クライアントはサーバにアクセスする度に、そのセットされたクッキーを送信します(ブラウザの仕様)
サーバ側ではクライアントが送ってきたセッションIDをチェックして、サーバに記録されているセッションIDと同じであれば同じクライアントのアクセスからだ、と判断します。
ショッピングカートを例に、実装の流れを考えてみましょう。
クライアントがカタログページにアクセスしてくる
・セッションIDを作成する
・クライアントのクッキーにセットする
クライアントが商品を注文した
・必要な情報を、セッションIDとセットにしてサーバで保存する
クライアントが買い物レジに移動した
・クライアントとサーバのセッションIDをチェックし、同じものを取り出して表示
以上のような流れとなります。
面倒そうですね。
上記の流れでは書いていませんが、実際には他にもセッションの有効期限を決めたり、不正なセッションIDがやってきた場合の処理など、実装しなければならないことがいろいろあります。
またセッションはセキュリティ上考慮すべき点も多いので、毎回考えながら行うのは大変です。
特にセッションIDが他者に漏れたりすると買い物され放題なので、実装には十分注意しなければなりません。
Perlではこのあたりの処理も自力で実行しなければなりません。
CGI::Session等のモジュールもありますが、それでも実装するにはやや敷居が高いのが実情です。
PHPでは、
session_start();
と書くだけであとはPHPが全ての管理を行ってくれます。
session_start();
と書くことで使えるようになるスーパーグローバル変数があります。
$_SESSIONです。
普通のローカル変数同様、普通に代入するだけで使えます。
自動的にクライアント毎に内容が振り分けられ、Perlで必要だったクライアントのセッションIDとサーバのセッションIDをチェックして云々、という処理が一切必要ないという恐ろしく便利な変数です。
フォームのスクリプトa.htmlとa.php、およびそれとは何の関係もないb.phpを用意します。
http://yuubiseiharukana.creativeroot.jp/php1/a.html
<form action="a.php" method="POST"> <input type="text" name="userid"> <input type="text" name="pass"> </form> |
http://yuubiseiharukana.creativeroot.jp/php1/a.php
<?php session_start(); $_SESSION['userid']=$_POST['userid'] $_SESSION['pass']=$_POST['pass'] ?> |
http://yuubiseiharukana.creativeroot.jp/php1/b.php
<?php session_start(); print_r($_SESSION); ?> |
a.htmlでフォームを登録した後、適当に他のサイトを見て回った後でb.phpにアクセスすると、a.phpでセットした変数がしっかり表示されます。
有効期限は設定で変更できますが、デフォルトはブラウザを閉じるまでです。
他にもセッション名を指定する関数session_name()やセッションIDを取得する変数session_id()等セッションに関するいろいろな関数がありますが、使用法についてはまたそのうち。
ちなみにsession_start();しないとsession_id()は取得できませんが、クッキー自体は毎回送信されているはずで、実際$_COOKIE['PHPSESSID']で取得できます。
#'0'=>'苗字','1'=>'名前','2'=>'ユーザID', '3'=>'パスワード','4'=>'郵便番号','5'=>'住所', '6'=>'メールアドレス','7'=>'ホームページ','8'=>'性別' $yamada=array('0'=>'山田','1'=>'太郎','2'=>'yamadax', '3'=>'pass','4'=>'111-1111','5'=>'', '6'=>'a@a.jp','7'=>'','8'=>'男'); |
ユーザIDを取得したいときは、
$yamada[2]
で取得できます。
さて、スクリプトの作成時はまだいいのですが、
時間をおいて見直すと、各数字が何を表しているのか間違いなくわからなくなります。
コメントに書いてはあるので代入時はともかく、
プログラムのずっと下のほうで$yamada[7]を使用していたとき、
$yamada[7]って何だっけ、などとなってしまうともう大変です。
そんなときのために連想配列というものが用意されています。
簡単に言うと数値のかわりに文字を使える配列です。
とりあえず書き換えてみましょう。
$yamada=array('firse_name'=>'山田', 'last_name'=>'太郎','user_id'=>'yamadax', 'user_pass'=>'pass','zip_code'=>'111-1111', 'address'=>'','mail_address'=>'a@a.jp', 'homepage_address'=>'','sex'=>'男'); |
ユーザIDを取得したいときは、
$yamada['user_id']
で取得できます。
これならプログラムのずっと下のほうに$yamada['homepage_address']があった場合、
$yamada['homepage_address']が何を表しているのか容易にわかるでしょう。
はて。
元々配列ってのが個別に変数名をつけると大変だから数値にしたというのに、
その数値をまた文字に直してどうするんだろう?
と思うかもしれません。
幸いなことにPHPでは連想配列をひと塊の変数と考えることができ、
その各要素に、あるいは全体にアクセスできる方法が揃っています。
例えばforeach文は、for文のように連想配列の各要素に対して処理を繰り返します。
foreach($yamada as $xxxxx => $yyyyy){ print('連想配列名:'.$xxxxx.' その値'.$yyyyy.'<br>'); } |
で全要素の名前とその値を表示、等ということを行うことが出来ます。
他にもarray_pushやarray_filter等の有用な関数が揃っているので、
いろいろ使ってみましょう。
中にはarray_product等の、いったい誰がこんなの使うんだ的な関数もありますが。
さて、山田さん以外に鈴木さんの情報を管理したいとしましょう。
$suzuki
を作るとなるとまた変数が増えます。
同姓同名の山田さんが増えたりするともう大変です。
そんなときのために多次元配列が使用可能となっています。
一見入り組んでて複雑に見えますが、やってることは今までの配列と同じです。
以下のように書くことが出来ます。
上は1行で書いてあり、下は2行で書いてありますが、中身は同じです。
$user_info=array( $user_info[]=array( |
$user_info[0]['first_name']で「山田」が取得でき、
$user_info[1]['last_name']で「次郎」が取得できます。
この書き方なら、いくら人数が増えても1次元目の数値を増やすだけで簡単に対応できます。
たとえば、全員に対してメアドがきちんと書かれているか空白のチェックを行いたい場合、
foreach($user_info as $xxxxx){ if(!$xxxxx['mail_address']){ print($xxxxx['firse_name'].'のメアドが空白'); } } |
というような書き方が出来ます。
たった数件ならどのような書き方をしても大してかわりませんが、
後々のことを考えると、できるだけループで処理できるコードを書くほうがよいでしょう。
多次元配列は便利ですが、使いすぎると逆にわかりにくくなる上にメモリを大量に使用するので、
せいぜい3次元程度にとどめておきましょう。
$a0='apple'; $a1='orange'; $a2='banana'; $a3='grape'; $a4='peach'; $a5='melon'; $a6='blueberry'; $a7='blackberry'; $a8='raspberry'; $a9='strawberry'; |
疲れました。
すべての値を表示したい場合は、以下のように書かねばなりません。
print($a0.'<br>'.$a1.'<br>'.$a2.'<br>'.$a3.'<br>'.$a4.'<br>' .$a5.'<br>'.$a6.'<br>'.$a7.'<br>'.$a8.'<br>'.$a9); |
10個の中から特定の文字を検索したい、となるともっと大変です。
$match_word='peach'; if($a0==$match_word){print('0');} elseif($a1==$match_word){print('1');} elseif($a2==$match_word){print('2');} elseif($a3==$match_word){print('3');} elseif($a4==$match_word){print('4');} elseif($a5==$match_word){print('5');} elseif($a6==$match_word){print('6');} elseif($a7==$match_word){print('7');} elseif($a8==$match_word){print('8');} elseif($a9==$match_word){print('9');} |
たとえば変数が100個になったら、などと考えると馬鹿馬鹿しすぎて実用になりません。
このような無駄を排除するため、ほぼ全ての言語には配列という機能が備わっています。
使用方法は慣れないと面倒かもしれませんが、考え方としては簡単です。
配列名に数字を使えるようにした、というだけです。
とりあえず書き換えてみましょう。
$a[0]='apple'; $a[1]='orange'; $a[2]='banana'; $a[3]='grape'; $a[4]='peach'; $a[5]='melon'; $a[6]='blueberry'; $a[7]='blackberry'; $a[8]='raspberry'; $a[9]='strawberry'; |
どこが変わったかというと、数字が[]で括られているというところだけです。
これで何が違うか、最大の利点は、[]内の数値に変数を入れることが出来るという点です。
表示、検索を配列で書き換えてみましょう。
for($i=0;$i<count($a);$i++){ |
このように大量の変数に対して繰り返し同じ処理を行いたい、
そのような場合に威力を発揮するのが配列です。
さて、表示検索は短く書き換えられたのに、定義だけは長文のままです。
もちろんこれも短く書ける方法があります。しかも複数。
以下の設定方法は、どちらも同じになります。
下のように設定する意味は次回わかるでしょう。きっと。
$a=array('apple','orange','banana','grape','peach','melon', |
配列を使用すると入力値の処理等が劇的に楽になりますので、是非使用法をマスターしましょう。
ちなみに上の表示検索処理ですが、もっと簡単な関数が用意されていたりします。
print_r($a); |
http://yuubiseiharukana.creativeroot.jp/php1/php1-4-1.php
<?php $x=1; $y=2; print($x+$y); ?> |
さあ何と表示されるでしょう?
$xというのは変数です。
中学校の数学を思い出してください。
y=2x+1
とかやりましたね。
文字式と言ってましたが、プログラミングでは変数と言います。
PHPでは(そして大半の言語では)頭に$をつけることで、変数を表します。
$x=1
とした場合、$xという変数に1という内容が詰め込まれる、ということです。
$xは文字ですが、中身は数値なので計算することが可能です。
従って、$x+$yという計算を行うと、答えは3となります。
つまり、上のプログラムは「3」と表示されます。
このような計算を用いた動的なページ生成、
これがPHPを用いてページを表示する最大の理由となります。
実のところHTMLだけでは買い物籠の中身を保持することすらできないので、
ちょっとでもインタラクティブなページを作成したいなら
他言語の助けを借りることが必須となります。
その手段がPHPでありPerlでありJavaScriptであるというわけです。
今のは最初から値を設定してあったわけですが、
今度はフォームから値を取得する、本格的な動的ページを作成していきましょう。
http://yuubiseiharukana.creativeroot.jp/php1/php1-4-2.php
<html> <head> </head> <body> <form method="POST" action=" <?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?> "> <input type="text" maxlength="20" name="shiki"><br /> <input type="submit" value="送信"> </form> <hr> <?php if($_POST['shiki']){ $x=htmlspecialchars($_POST['shiki']); print($x); } ?> </body> </html> |
テキスト欄に入力した文字を、ただ単に表示するだけの内容です。
たったこれだけでも、HTMLだけでは不可能な処理です。
カウンターもブログもSNSも、すべてはこの入力フォームがスタート地点です。
徐々に規模を大きくしていきましょう。
$_POSTは、フォームで送信した内容を受け取ることができる特殊な変数で、
PHPに予め用意されている「スーパーグローバル変数」と呼ばれるものの一種です。
$_POST['shiki']
と書くだけで、フォームのname="shiki"から送られてきた値を取得することができます。
これはもうPHPには無くてはならない機能です。
同様なスーパーグローバル変数に、$_GET,$_SESSION,$_SERVER等がありますが、
どれも便利なので徐々に使えるようになっていきましょう。
ちなみに、htmlspecialcharsは、PHPを扱う上でprintより大切な関数です。
画面表示を行う場合、必ず使ってください。必ずです。
詳しくはクロスサイトスクリプティングの章を参考。
http://yuubiseiharukana.blog.shinobi.jp/Entry/4/
フォームの送り先が、
<?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?>
とすごいことになってますが、これは「自分自身」を表しています。
とりあえず今の段階では、とりあえずこれをコピペして使うようにしてください。
別にaction="./php1-4-2.php"とファイル名を直接指定しても構わないのですが、
今後ファイル名を変更したりフォルダを移動したりした場合、
書き換えが面倒だったり書き換え忘れて不具合が出たりすると不便なので、
動的に作成できる部分は動的に作りましょう。
最後に簡単な計算プログラムを置いておきます。
入力値の処理が非常に甘いので、数式以外を入力すると致命的エラーになります。
ぜひ入力値をチェックするようにカスタマイズしてみましょう。
http://yuubiseiharukana.creativeroot.jp/php1/php1-4-3.php
<html> <head> </head> <body> <form method="POST" action=" <?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?> "> <input type="text" maxlength="20" name="shiki" value="<?php print($_POST['shiki']); ?>">←数式を入力<br /> <input type="submit" value="計算する"> </form> <hr> <?php if($_POST['shiki']){ $x=htmlspecialchars($_POST['shiki']); print($x.'='); eval("print $x;"); } ?> </body> </html> |
まあ、この程度の内容ならJavaScriptのほうが手っ取り早いのですが、それはまたそのうち。
大量のチェックすボックスが存在する掲示板やメールの管理フォームには、
よくJavaScriptによる「すべて選択」「選択解除」ボタンが実装されています。
これをPHPと併用すると問題が発生します。
<form method="GET"> <input type="checkbox" name="aaaaa" value="1"> <input type="checkbox" name="aaaaa" value="2"> <input type="checkbox" name="aaaaa" value="3"> <input type="button" name="aaa" value="すべて選択" onClick="check(true);"> <input type="button" name="aaa" value="選択解除" onClick="check(false);"> |
aaaaaというnameに複数の値を設定し、valueで区別するというよく見るフォームです。
name="aaaaa"の配列に値が全て入っているので、
JavaScript側ではlengthで長さを取得し、forで回すだけで簡単に実装できます。
function check(check){ for(var count=0;count<document.forms[0].aaaaa.length;count++){ document.forms[0].aaaaa[count].checked=check; } } |
さて、このフォームをPHPに渡すと重大な問題が表れます。
本来HTMLでは同一nameで複数のvalueを許可しており、
http://www.asahi-net.or.jp/%7Esd5a-ucd/rec-html401j/interact/forms.html#checkbox
実際送信先に渡されるパラメータも?aaaaa=1&aaaaa=2&aaaaa=3
という形になっていますが、
$_GETで受け取ると、「最後の一個の値しか取得できない」という不具合仕様が現れます。
この場合$_GET['aaaaa']=3となってしまい、
aaaaa=1やaaaaa=2の部分から値を取得することは出来ません。
文字列としてならば、$_SERVER["QUERY_STRING"]で"aaaaa=1&aaaaa=2&aaaaa=3"という値を取得することができます。
これはURLの?以降の部分となります。
method="POST"だと内部構造がどうなってるのかはよくわかりませんが、
$_POSTで受け取れる値はやはり最後の一個だけです。
こちらの場合はURLが変化するわけではないので、$_SERVER["QUERY_STRING"]を使ってもaaaaa=1の値を受け取ることはできません。
ちなみにPerlでは配列で受け取ることが可能です。
PHPの仕様で、どうにもならないのでHTML側を書き換えるしかありません。
<input type="checkbox" name="aaaaa[]" value="1"> <input type="checkbox" name="aaaaa[]" value="2"> <input type="checkbox" name="aaaaa[]" value="3"> |
とすると、PHPで受け取った場合[]内に数値が自動的に入り、
$_GET['aaaaa'][0]等で各値を無事受け取れるようになります。
ところがこうすると今度はJavaScript側で問題が発生します。
JavaScriptはaaaaa[]を配列として見てくれないので、
よってlengthで要素数を取得できません。
またdocument.forms[0].aaaaa[0]等とやっても値は取得できません。
結論から言うと下記で取得できます。
document.forms[0].elements['aaaaa[]']
elementsにはフォーム内の全要素が入るので、
その中からaaaaa[]の集合を抜き出しています。
従って、PHPとJavaScriptで同じnameの要素を使用したい場合、
JavaScript側で、「aaaaa」を「elements['aaaaa[]'」と書き換えると動きます。
function comment_check(check){ for(var count=0;count<document.forms[0]. elements['aaaaa[]'].length;count++){ document.forms[0].elements['aaaaa[]'][count].checked=check; } } |
思うにPHPの作者はJavaScriptが嫌い。
外部からの入力は、必ず汚染されているものだという前提で処理を行う必要があります。
xss.php
<?php print($_GET['入力']) ?> |
xss.phpにはクロスサイトスクリプティングに対する処理が行われていないので、
xss.php?入力=<script>○○
等と好き放題行えます。
このことに関する記事はちょっと探せば大量に出てきます。
さて、xssのチェックを行う関数xss_check()を実装したとして、以下のような処理を行ってみます。
http://hogehoge.com/a.php
$data=$_GET['data'] $check=xss_check($data); if($check=='OK'){ include_once 'ok.php'; }else{ include_once 'ng.php'; } |
xss_check関数で問題が無ければok.phpを呼び出し、駄目であればng.phpを呼び出すという処理です。
ok.php内では$dataを画面表示したりDBに登録したりする処理を行っています。
見た目もわかりやすいですし入力値のチェックも行っているので問題ないように見えます。
ところでincludeは外部サーバのファイルも取得することが出来ます。
下のコードをちょっと見てください。
http://xxxxxx.com/b.php
$data='危険なコード'; include_once 'http://hogehoge.com/ok.php'; |
何のチェックも通さずにok.phpが実行されてしまいました。
b.phpのカレントディレクトリはxxxxx.comになるので、
hogehoge.comがカレントディレクトリという前提で動いているa.phpは、大抵の場合動作しません。
しかしchdir等でうまく場所を合わせられたり、a.phpが外部からincludeされる前提で作成されたりしていた場合など、
動いてしまう可能性も0ではありません。
もちろんb.phpを動かすためにはa.phpの構造を知らないといけないので
そう簡単に出来るわけではありませんが、
外部から操作できるという点では明らかにセキュリティホールになり得ます。
この手の情報ってあんまり見たことないんですが気のせい?
中身は
Hello World |
それ以外のものがくっついていたとしたら、
無料サーバにはありがちな自動で付加される広告なので気にしない方向で。
前回書いたプログラムがどうやってHTMLになったのでしょうか。
1.<?php 2. print("Hello,world"); 3.?> |
まず1,3行目の<?php ?>ですが、ここからここまでがPHPですよ、という目印です。
プログラムを書くときは必ず<?php ?>でくくってから書く必要があります。
逆に言うと括弧の外はPHPではないわけで、では何と判断されるというかというとHTMLです。
ちょっと試してみましょう。
以下のファイルを『index2.php』としてアップします。
http://yuubiseiharukana.creativeroot.jp/index2.php
<html> <head> </head> <body> Hello,world </body> </html> |
文書型宣言とか色々必要なものを飛ばしているので良くない内容なのですが、とりあえず動きます。
どう表示されるかというと、拡張子は.phpなのに極めて普通にHTMLが出力されています。
もうひとつ試してみましょう。今度はindex3.phpとしましょう。
こんな適当な名付け方していると後で泣きを見る羽目になるのですが、
とりあえずは気にしない方向で。
http://yuubiseiharukana.creativeroot.jp/index3.php
<html> <head> </head> <body> <?php print("Hello,world"); ?> </body> </html> |
表示結果はindex2.phpとまったく一緒、一字一句の違いもありません(スペースの数は違うかも)
もうおわかりですね。
サーバはphpファイルを読み、<?php ?>の中身だけを解釈し、
テキストに変換してからブラウザに渡すわけです。
遅れましたがprint();の説明。これはもう見たまんま、()の中身を表示せよの意味です。
print http://jp.php.net/print
色々難しいことが書いてありますがおいおいわかってきます。
今はシングルクォーテーション(ダブルクォーテーションでもいい)で囲った文字が表示されるとだけ。
結論として、
<?php print("Hello,world"); ?>
はサーバで解釈され、文字列
Hello,world
になる、ということです。
そんな文字を表示するだけなのにいちいちプログラム書かないといけないの?
と思うかもしれませんが、
次回の記事で早速その思いは打ち砕かれることでしょう。
たぶん。
今日はとりあえずPHPマニュアルをブックマークしておくように。
PHPプログラマの必需品です。
http://jp.php.net/manual/ja/index.php
さあとりあえずHelloWorldを作ってみましょう。
どういう理由かは知りませんが、新しい言語を始めるときは
最初に必ず「Hello,World」と表示するプログラムを作る、
というのが鉄の掟となっているので従うことにしましょう。
テキストエディタで以下の内容を書き込み、『hello_world.php』と名前をつけて保存します。
<?php print("Hello,world"); ?> |
さあブラウザで開いてみましょう。
・・・開けませんね。
実はブラウザはPHP言語を理解できません。
PHPを解釈するのはサーバなのです。
サーバがPHPをHTMLに翻訳して出力し、ブラウザがそのHTMLを表示するのです。
というわけでPHPを理解してくれるサーバが必要になります。
Windows上で作る方法もありますが、初心者には荷が重いのでてっとりばやくPHPを利用できるレンタルスペースを探しましょう。
有名どころではさくらインターネットがあります。
バックボーンがしっかりしているのでサイトを本格運用するならいいのですが、有料です。
とりあえず試してみたいだけなので無料のところを探しましょう。
http://www.kooss.com/等で適当に探してください。
理由はそのうち言いますが、PHP以外にSQLも使用可能なところをお奨めします。
今回はここを使用してみました。
http://www.creativeroot.jp/
登録が終わったら先ほど作ったindex.htmlをFTPでアップロードします。
さて、ブラウザで見てみましょう。
http://yuubiseiharukana.creativeroot.jp/hello_world.php
無事に表示されましたか?
実に簡単ですね。
次回はプログラムの解説でも行いましょう。
とその前に大事な大事なセキュリティの話。
ブラウザのアドレス欄からファイル名を削除して
http://yuubiseiharukana.creativeroot.jp/
を表示してみてください。
サーバが用意しているエラーファイルが表示されるはずです。
その場合はひとまず今回は終了です。
滅多に無いはずですが、万が一ファイル名の一覧が表示されてしまった場合、
『index.html』という名前の空のファイルを置いておきましょう。
・・・この場合対策してないサーバなので危険なんですがね。
「超簡単にウェブプログラムできる言語」
と言える。
とりあえず動的なホームページを作成したい、というときに真っ先に選ぶべき言語である。
同じ立ち位置の言語としてはPerl、いわゆるCGIが存在するが、
元々PerlはWWW向けに作られた言語ではないので、
Perlでウェブプログラムを作成するには色々と面倒なところがある。
その点PHPはサーバにインストールさえされていれば即日始められてしまうという超難易度。
さてウェブプログラムと一口に言っても、有名どころでさえPHP、Perl、CSS、Java、JavaScript、RubyにPythonにC++等々大量に言語が存在する。
このうちCSSとJavaScriptはクライアント側、要するにブラウザで解釈される言語であるので競合はしない。うまく利用し合うことが必要である。
Javaはぶっちゃけ言語仕様に問題がある。byte $a=1;byte $b=$a+1;と書いただけでエラーになる言語なんぞ使いたくもないわ。
RubyにPythonを使うくらいならPerlを使うわ。人口的な問題で。
C++を使えるような人はこんなところ見る必要ありませんから。帰れ。
と激しく独断的な理由でPHPを使用することにする。
次回はとりあえずHelloWorldだ。