第6週


リクエスト情報 / DB連携

リクエスト情報

リクエスト情報:クライアントからサーバに送信される情報

HTTP(HyperText? Transfer Protocol)

サーバとクライアントがお互いに通信する際に利用するプロトコルのこと

HTTP通信の構成

分類項目概要
HTTPリクエストHTTPメッソドサーバに対する直接の要求と取得するパス
リクエストヘッダリクエストの構成情報、クライアント情報etc
リクエスト本体フォームから送信されたデータ
レスポンスHTTPステータスサーバーでの処理結果を表すメッセージ
レスポンスヘッダコンテンツの構成情報、サーバー情報etc
レスポンス本体コンテンツ本体

HTTPメソッドとHTTPステータス

  • HTTPメッソド:クライアントからサーバに対して発行する直接の命令で、
            メソッド名、コンテンツへパス、プロトコル情報で構成される
  • HTTPステータス:サーバでの処理結果を表す

HTTPステータス

分類HTTPステータス意味
100(情報)100(Continue)継続可能
200(成功)200(OK)成功
201(Created)成功(サーバー側に新しいリソースを生成)
202(Accepted)受付完了(未処理)
300(リダイレクト)301(Moved Permanently)リソースが恒久的に移動した
302(Moved Temporarily)リソースが一時的に移動した
303(See Other)リソースが別の場所に存在する
304(Not Modified)リソースが変更されてない
400(クライアントエラー)400(Bad Request)不正なリクエスト
401(Unauthorized)HTTP認証要求
403(Forbidden)アクセスを拒否
404(Not Found)リソースが見つからない
405(Method Not Allowed)HTTPメソッドが不許可
407(Proxy Authentication Required)プロキシで認証の必要がある
408(Request Time-out)リクエストタイムアウト
500(サーバーエラー)500(Internal Server Error)サーバエラー
501(Not Implemented)応答に必要な機能が未実装
503(Service Unavailable)HTTPサーバが利用不可

スーパーグローバル変数

リクエスト情報を取得/操作するための変数
必要なリクエスト情報はPHP側で自動的に解析し、用意するので、
スクリプト側ではこれを参照するだけでよい。


$_POST

ポストデータとは、
「method="POST"」で定義されたHTMLフォームから送信されるデータのこと。
クライアントから送信されたポストデータを取得するのが、$_POSTの役割。
これは連想配列で、各要素にアクセスするには$_POST['要素名']というように記述する

エスケープ処理

ユーザーから入力された値やDB/外部サービスなどから取得した値を表示する場合には、
HTMLエスケープを行う必要がある。
文字列に含まれる「<」「>」「&」「"」などの文字列を、
「<」「>」「&」「&quot」の文字に置き換える処理のことを言う。

これを行うことによって、たとえば「<」はタグの始まりを意味するのではなく、
本来の「<」という記号として認識するため、サイトクロススクリプティング(XXS)対策になる。

エスケープを行うには以下の関数を使う

string htmlspecialchars(string $str [, int $style = ENT_COMPAT [, string $char]])
    $strをエスケープする。
    原則として、$style = ENT_QUOTES と指定するのが一般的。
    なお関数名が長く、利用頻度も高いので、ユーザー定義関数として、
    短い名前に直しておく方がよい

PHP-Manual htmlspecialchars

複数の値を持つ要素にアクセスする

サンプル1

$_GET

クエリ情報とは、
URLの末尾「?」以降に「キー名=値」のセットで付加される簡単な情報。
URLのパス本体とクエリ情報は「?」で区切られ、複数のキーがある場合は「&」で連結される。

URLに直接指定するほか、フォームタグにGETオプションを指定する方法がある。

※ただし、パス本体とクエリ情報の区切り文字である「?」や「&」、「%」、
 空白文字やマルチバイト文字をクエリ文字に含める場合は予めURLエンコードをしておく必要がある。

string urlencode (string $str)
    文字列を URL エンコードする

PHP:Manual urlencode

ポストデータとクエリ情報の違い


データを格納している位置

クエリ情報はデータをURLに載せて送信しているのに対し、
ポストデータはリクエストボディと呼ばれるブロックに格納してデータのやり取りを行う。

クエリ情報で送信できるデータサイズには制限がある

ポストデータはサイズが事実上無制限であるのに対して、 クエリ情報はURLにデータを載せているため、ブラウザやサーバ環境の制限を受ける

クエリ情報ではデータがアドレス欄に表示されてしまう

セキュリティ上よくない。
ただし、だからといって「データを保護すること」が目的でポストデータを使うのは誤り。
データを保護するならば「SSL(Secure Socket Layer)」のような暗号化通信をする必要がある。

$_SERVER

普段我々が目にしない不可視な情報
(例えば、ブラウザの種類や対応する言語、リンク元のページ etc)などの様々な情報が
内部的に生成されてサーバーに送信されている。
こういった情報のことをヘッダ情報という。

ヘッダ情報の種類(一部)

種類ヘッダ名概要
一般ヘッダCache-Controlキャッシュルールを規定する
Connectionプロキシサーバで削除すべきHTTPヘッダを指定
Dateコンテンツを生成した日時
Progmaキャッシングを利用するか
Transfer-Encodingコンテンツの転送エンコーディング方式
リクエストAcceptクライアントがサポートしているコンテンツ
Accept-Languageクライアントが対応している言語
Authorization認証情報
Cookieクライアントに保存されたクッキ−
Host要求先のホスト名
Proxy-Authorizationプロキシサーバ用認証情報
Range要求するリソース
Refererリンク元のURL
User-Agentクライアントの種類
レスポンスETagリソースを一意に特定するためのキー
Locationクライアントに新しいURIに移動するように促す
Serverクライアントにクッキーを送信
エンティティContent-Encodingコンテンツエンコーディング
Content-Lengthコンテンツのサイズ
Content-Typeコンテンツの種類
Expiresコンテンツの有効期限
Last-Modifiedコンテンツの最終更新年月

上記のヘッダ情報を扱うには、
以下の$_SERVER経由でアクセスできるサーバ変数を使う

PHP:Manual $_SERVER


レスポンスヘッダの設定

ヘッダ情報は
「クライアントからサーバに送られるリクエストヘッダ」
「サーバからクライアントに送られるレスポンスヘッダ」に大別できる。

PHPスクリプトからレスポンスヘッダを操作することも可能

リダイレクト

リダイレクトを行うには、以下の関数を使う

void header(string $head [, bool $rep = TRUE [, int $code]])
    生の HTTP ヘッダを送信する
    header()関数は、 通常のHTMLタグまたはPHPからの出力にかかわらず、すべての実際の出力の前にコールする必要がある

上記の関数の「$head」部分にURLを書く

※実際に裏で行われていること

1. header関数が書かれたページが、サーバに要求される
       ↓
2. サーバがページを処理して、Locationヘッダを発行し、クライアントに情報を返す
       ↓
3. クライアントからLocationヘッダに書かれたURLにアクセスしようとして、再びサーバに自動要求
       ↓
4. 結果ページへアクセス

リダイレクトは一つのリクエスト処理に見えるが、
HTTPの観点では「2回リクエスト」が自動で行われている

キャッシュ処理を無効にする

header関数の「$head」部分に「ページの有効期限」をかく(Expires〜)

さまざまな種類のファイルを生成する

header関数の「$head」部分でContent-typeを指定する

$_SVN

サーバ側で設定されている環境変数を取得するためのスーパーグローバル変数
環境変数とは、コンピュータ上に予め定義されたパラメータのことで、
プログラムを実行する際に参照するパスやオプション値などを設定する


$_COOKIE

Cookieとは、クライアント側に保存可能な小さなテキストのこと。
HTTPはクライアントからの要求に対してサーバがレスポンスを返して終わりだった。
これをHTTPは「ステートレス(状態を保存できない)」なプロトコル。

しかし、Cookieを利用することで「クライアント単位で維持したい情報」の管理が容易になる。

Cookieに値を保存するには以下の関数を使う

bool setcookie(string $name [, string $valu [, int $expire = 0 [, string $path
                 [, string $domain [, bool $secure = FALSE [, bool $httponly = FALSE]]]]]])
    $nameにはCookieの名前、$valueにはCookieにセットする値
    他にも有効なドメインやパス、SSLの通信要求などがある

PHP:Manual setcookie

HTTPCookeiの有効化(PHP5.2〜)

第7引数に関してをTRUEにすることによって、HTTPCookeiが有効になり、
HTTP経由でのみアクセス可能となる。
JavaScript?経由ではアクセスできない。

$_SESSION

先のCookeiには以下のような問題点がある

  • データがクライアント側で保存される
     クライアント側でCookeiを受け入れないように設定できたり、保存したCookeiを改ざんしたり削除したりも出来るので、
     Cookeiから得られたデータには危険が伴う
  • 実データがネットワーク上を流れる
     Cookeiはヘッダー経由で受け渡しされれるため

上記の理由から、セッションというCookieよりもよりセキュアな手段を利用する。

セッションの利用

セッションを利用するには、下記の関数を最初に呼び出す必要がある

bool session_start()

セッションはCookeiでセッションIDを管理して、そのセッションIDをクラインとに発行する。
そのためサーバ側でデータが保存されるので、ネットワーク上に実データが流れないため
Cookeiを単体で使うよりも安全である。

セッションの利用の注意点

セッションはCookeiよりも安全にデータを保存できるが、
その反面サーバに負担をかけやすいという問題もある。

そのため不要になったセッションは迅速に、確実に破棄する必要がある。
これはサーバへ負担の面だけではなく、セキュリティの関係からも重要。

以下、セッションの破棄の方法

// セッション変数を全て解除する
$_SESSION = array();
 
// セッションを切断するにはセッションクッキーも削除する。
// Note: セッション情報だけでなくセッションを破壊する。
if (isset($_COOKIE[session_name()])) {
   setcookie(session_name(), '', time()-42000, '/');
}
 
// 最終的に、セッションを破壊する
session_destroy();

また、必ずしも上の動作を実行できるとは限らない。

例. エンドユーザーが勝手にブラウザを閉じてアプリケーションを終了

この場合は、サーバ側にセッション情報が残ったままだが、
これは永久に残るわけではなく、セッションの有効期限が過ぎれば自然と消滅する。
なお、セッションの有効期限はphp.iniで設定可能。


$_FILES

$_FILESはアップロードしたファイルに関する情報を取得するためのスーパーグローバル変数。

formタグにenctyoe="multipart/form-data"を指定してファイルのアップ用formを作成。

いろいろごにょごにょした後に、 move_uploaded_fileでアップロード中の一時フォルダから、
本来の保存先にファイルを移動すればok!

DB連携

DBの種類

  • カード型DB
    1つの情報を1枚のカードとして扱う
  • ネットワーク型DB
    関連するデータ同士が網状に繋がっている
  • リレーショナルDB
    よく使われている、皆さんが想像しやすいもの。
    表みたいな感じのDB
  • オブジェクト指向DB
    オブジェクトの形式で表現されるデータを格納するデータベース
  • Key-Value型DB
    キー/値のセットでデータを管理
    複雑な検索は不向きだが、スケーラビリティに優れ、クラウド環境で使用されることが多い
    有名どころでは「Tokyo Tyrant」とか

※大まかなDB関係、SQL関係は今回カット

以下、PHPとの連携について

DB抽象レイヤ

さまざまな種類のDBごとの違いを吸収して、アプリケーションがDBにアクセスする際の
手段を共通化するもの

代表的なDB抽象レイヤ

PEAR::DB、PEAR::MDB/MDB2、PEAR::DB_Objects、PDO、Zend_Db、dbx

以下、シンプルな構文仕様で処理速度の速いPDO(PHP Data Objects)を使用する。
なお、PDOはコアモジュールドライバに分けられ、
そのドライバを変えることでいろいろなDBにアクセス可能になる

DB との接続、接続オプション

PDOの場合、DBとの接続を管理するのはPDOクラスの役割。
まずはPDOクラスをインスタンス化させる

new PDO(string $dns [, string $user [, string $password [, array $option]]])
    $dsn:データベース接続文字列
    $user:接続ユーザ名
    $password:接続時のパスワード
    $option:接続オプション(オプション名=>設定値)

例. サンプル2

また、DB接続時の挙動を決めるパラメータのことを接続オプションと言い、
PDOクラスをインスタンス化する場合に連想配列「オプション名=>設定値」の形で指定するか、
以下のsetAttributeメソッドを使って個別に設定する

bool PDO::setAttribute(int $attr, mixed $value)
    $attr:オプション名
    $value:設定値

PHP:Manual setAttribute

※ドライバ固有のオプションもあるので、あまり積極的な使用は進められない


try〜catch命令

PDOクラスは何かしらの理由で接続できなかった場合、PDOクラスはPDOException例外を投げる。
このような例外が発生したときに行う処理を例外処理といい、
例外処理を行う場合にはtry〜catch命令を使う

try {
    // 例外が発生するコード
} catch(発生するかもしれない例外の種類 例外を受け取る変数名) {
    // 例外発生処理
}

例外が発生すると、エラーなどが起こってスクリプトが終了してしまうことがあるが、
この命令を使えば、catchブロックに引き継がれるので処理を継続できる

なお、例外はクラスの一種で、
catchブロックに渡された変数を介して例外に関するさまざまな情報にアクセスできる。

以下、利用可能なメッソド

メソッド概要
getMessage()例外メッセージ
getCode()例外コード
getFile()例外が発生したファイル名
getLine()例外が発生した行数
getTrace()バックトレース(配列)
getTraceAsString?()バックとレース(文字列)

なお、例外クラスについては、次週もう一度やる

PDOを使ったSQLクエリの発行

例.

$db = new PDO(〜); //DBと接続確立
$stt = $db->prepare('INSERT INTO book (title, author) VALUES (:title, :author)');
$stt->bindValue(:title, $title);
$stt->bindValue(:author, $author);
$stt->execute();

PDOを使ってSQLを発行する時は、PDOStatementオブジェクトを使う
PDOStatementオブジェクトは、PDOオブジェクト$dbからprepareメソッドを呼び出すことで取得できる

prepaerメッソド
PDO::prepare(string $statement [, array $options])
    $statement:発行するSQL
    $options:接続オプション

また、SQLに動的にパラメータを入れる場合には、
SQLにプレイスホルダと呼ばれるパラメータを置く場所をつくり、
以下のbindValueメッソドでプレイスホルダに具体的な値をセットしていく

bindValueメソッド
bool PDO::bindValue(mixed $param, mixed $value [, int $type = PDO::PARAM_STR])
    $param:パラメータ名
    $value:パラメータ値
    $type:データ型

なお、このプレイスホルダはSQL中ですべて?にしてもよく、
bindValueメッソドパラメータ名には、SQLで現れた順番の数字を入れていく
これを名前なしパラメータという

しかし、この方法はシンプルに記述できるものの、パラメータと値の関係わかりにくいため、
多用すべきではない

このあと、executeメッソドによって、DBに命令が送信される

queryメソッド

上記のようなパラメータが必要でないSQL(固定的なSQL)の場合には queryメソッドを使用することができる。

queryメソッドの引数の中にSQLを書けばよい

例. サンプル3

オートインクリメント値の取得

lastInsertIdメッソド
string PDO::lastInsertId([string $name])
    $name:シーケンス名

シーケンス名:DBで一意な数値キーを生成するために利用しする
         PostgreSQLやOracleのようなDBで利用する

結果セットの取得

結果セット:SQLのSELECTによりテーブルから取り出された、
      レコードを保持するためにメモリ上に用意された仮想テーブルのこと

この結果セットを取得するのが、prepare/executeメソッドの役割
executeメソッドは、取得した結果セットを自分自身にセットする。

この結果セットを読み込んでいく場合には、レコードごとに読み込んでいく必要があり、
現在読み込み可能なレコードを表す内部的な目印のことをレコードポインタといい、
レコードポインタが表す現在のレコードのことをカレントレコードという

executeメソッドによって結果セットが生成されたタイミングではレコードポインタは「先頭の行の直前」をさしている

PDOStatementのfetchメソッドはレコードポインタを次のポインタに移動しながら、~カレントレコードに含まれるフィードを読み込むメソッドで、
次のレコードに移動できない場合(次のレコードがない場合)、featchメソッドはFALSEを返す。

その性質を利用して、featchメソッドがFALSEを返すまでwhileループを繰り返すことで、
結果セット内の全てのレコードを取得する。

fetchメソッド
mixed PDO::fetch([int $style = PDO**FETCH_BOTH])
    $style:フェッチモード
定数取得形式取得値
PDO::FETCH_ASSOC連想配列$row['name']
PDO::FETCH_NUM一般配列$row[0]
PDO::FETCH_BOTH一般配列/連想配列row[0]/row['name']
PDO::FETCH_NAMED連想配列$row['name']
PDO::FETCH_OBJオブジェクト$row->name
PDO::FETCH_COLUMNスカラー値$row
PDO::FETCH_BOUNDbindColumnメソッドでバインドされたPHP変数$name
PDO::FETCH_CLASS指定されたクラスにフェッチ$row->name

また、フェッチメソッドにはいろいろ種類があるので、それらを使い分ける

fetchAllメソッド
mixed PDO::fetchAll([int $style = PDO::FETCH_BOTH])
    $style:フェッチモード
    結果セットの内容をまとめて配列に変換できる
fetchColumnメソッド
mixed PDO::fetchColumn([int $column = 0])
    $column:取得するインデックス番号
    結果セットの指定の列のみ取得する
fetchObjectメソッド
mixed PDO::fetchObject([string $class [, array $args]])
    $class:フェッチ先となるクラス名
    $args:クラスをインスタンス化する際に引き渡すパラメータ

パラメータのバインド

PDOにはパラメータをバインドするメソッドには以下の2種類があり、
それぞれ以下のように使い分ける。

  • bindValue
      値を直接バインドするときに使う
  • bindParam
      指定された変数の内容をバインドする。   言い換えれば、bindParamメソッドはバインドされたその瞬間ではなく、
      executeメソッドが呼び出されたタイミングで評価される
bindParamメソッド
bool PDO::bindParam(mixed $param, mixed &$value [, int $type [, int $len]])
    $param:パラメータ名
    $value:パラメータ値
    $type:データ型
    $len:データ長

トランザクション処理

関連する複数の処理をグループ化したもの。
グループ化した処理に関しては、 このグループ化した複数の処理を実行することを「トランザクション処理」といい、
トランザクション処理を確定することを「コミット」という。 また、トランザクション処理は「すべて成功」か「すべて失敗」かであることを保証するたまの処理であり、
トランザクション処理中のどこかで失敗が起きると、全ての処理を元に戻す。
これを「ロールバック」という。

PDOにおいてトランザクション処理を行うには、
beginTransactionメソッドcommitメソッドとの間にトランザクション処理を入れる。
また、ロールバックを行うにはrollBackメソッドを使う

beginTransactionメソッド
bool PDO::beginTransaction(void)
    オートコミットモードをオフにする。
    オートコミットモードがオフの間、PDOオブジェクトを通じてデータベースに加えた変更は、PDO::commit()をコールするまでコミットされない。
    PDO::rollback()をコールすると、データベースへの全ての変更をロールバックし、
    オートコミットモードに設定された接続を返す。
    MySQLを含むいくつかのDBでは、「DROP TABLE」や「CREATE TABLE」のようなDB定義言語 (DDL)ステートメントがトランザクション中に発行される場合、
    暗黙的なコミットが自動的に発行される。
    この暗黙的なコミットにより、そのトランザクション境界で他のあらゆる変更をロールバックすることができなくなる。 

参考資料

  • 独習PHP 第2版