PHP/MySQL – トランザクション確認(PDO)

概要

PHPのオブジェクト型(PDO)でMySQLのトランザクションを確認。

トランザクション中の動作はデータベースステートメントには設定されるが、トランザクション終了後のコミット/ロールバックによって設定結果がデータベースに反映されるかどうかが決まる。

確認コード

  1. 5行目でトランザクションでコミットフラグをtrueにセット
  2. 通常モードで’one’を書き込み
  3. 14行目でトランザクション開始
  4. ‘two’, ‘three’を書き込み
  5. 25行目でコミット、トランザクション確定
    • 通常モードへは自動的に復帰

確認結果

以下、出力を見ながらトランザクションの動作を確認。

  • 通常モードでは普通に書き込み
  • トランザクション開始後、データベースステートメントの内容は指示通り設定
  • トランザクション終了後、データベース内容の変更が確認できる
  • 通常モード復帰後も普通に書き込み

以下は出力結果で、終了後のデータベースの内容は’one’, ‘two’, ‘three’, ‘four’となる。

次にコミットフラグを$commit=falseにすると、commit()メソッドではなくrollback()メソッドが実行され、実行結果は以下のようになる。

  • 通常モードで普通に’one’が書き込まれる
  • トランザクションに入り、データベースステートメントには’two’, ‘three’が設定される
  • トランザクション終了後にロールバックされると、データベースステートメントがデータベースに反映されてもトランザクション中の’two’, ‘three’は書き込まれない
  • 通常モードに復帰後’four’が書き込まれる

 

PHP/MySQL – トランザクション確認(mysqli)

概要

PHPの手続き型(mysqli)でMySQLのトランザクションを確認。

トランザクション中の動作はデータベースリンクには設定されるが、トランザクション終了後のコミット/ロールバックによって設定結果がデータベースに反映されるかどうかが決まる。

確認コード

  1. 7行目でトランザクションでコミットフラグをtrueにセット
  2. 通常モードで’one’を書き込み
  3. 20行目でトランザクション開始
  4. ‘two’, ‘three’を書き込み
  5. 33行目でコミット、トランザクション確定
  6. 41行目で通常モードに復帰

確認結果

以下、出力を見ながらトランザクションの動作を確認。

  • 通常モードでは普通に書き込み
  • トランザクション開始後、データベースリンクの内容は指示通り設定
  • トランザクション終了後、データベース内容の変更が確認できる
  • 通常モード復帰後も普通に書き込み

以下は出力結果で、終了後のデータベースの内容は’one’, ‘two’, ‘three’, ‘four’となる。

次にコミットフラグを$commit=falseにすると、msqli_commit()ではなくmsqli_rollback()が実行され、実行結果は以下のようになる。

  • 通常モードで普通に’one’が書き込まれる
  • トランザクションに入り、データベースリンクには’two’, ‘three’が設定される
  • トランザクション終了後にロールバックされると、データベースリンクがデータベースに反映されてもトランザクション中の’two’, ‘three’は書き込まれない
  • 通常モードに復帰後’four’が書き込まれる

 

PHP – PDOによるデータベース操作

概要

PDO(PHP Database Object)によるデータベース操作は、手続き型に比べて、異なるDBMS間の際を隠蔽・吸収してくれるほか、セキュリティー対策も組み込まれている。

基本の流れ

PDOの最も基本的な流れは以下の通り。

  1. ユーザー情報やデータベース情報からPDOオブジェクトを生成する
  2. PDO::query()メソッドにSQLクエリーを渡してPDOStatementオブジェクトを得る
  3. PDOStatementオブジェクトからデータをフェッチする

例外処理などを無視した骨格は以下の通り。

メソッド

PDO::__constructor

PDOのコンストラクター

public PDO::__construct ( string $dsn , string $username = ? , string $passwd = ? , array $options = ? )

dsn

データソースネーム(Data Source Name)。DBMS名やホストなどの情報を文字列で指定する。たとえばlocalhostのMySQLを文字セット・データベースまで含めて指定する場合は以下のように記述する。

'mysql:host=localhost;charset=utf8mb4;dbname=databasename'

charsetで文字コードセットをしている点に注意。

$options

PDOクラス定数と用いた連想配列で指定する。たとえばエラーを例外としてthrowさせたい場合は以下のように指定する。

array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)

PDO::query

クエリー文字列を与えて、検索結果をPDOStatementオブジェクトとして受け取る。

PDOStatement::fetch()

検索結果を取り出す。一般的なのは、while文の条件の中でfetch()メソッドの結果を取り出して、全レコードについて処理を行うケース。以下の例では、クエリーによって抽出された都道府県と市町村を連想配列の配列として格納する。

PDO::prepare

PDO::query()による方法は、SQLステートメントがそのまま実行されるためセキュリティー上脆弱。

PDO::prepare()によるプリペアードステートメントとbindValue/bindParamによる値や変数のバインドによれば特定文字のエスケープが行われるため、こちらを使うべき。

PDO::lastInsertId

最後に登録されたレコードのIDを返す。MySQLの場合引数は不要。戻り値はstring。

雛形

PDO::query

レコード数のカウント

SQLのCOUNTの結果1行からデータを読む。以下の例では、id列をカウントした結果が1行で返されるのをfetchColumn()でカラム指定してデータを取得している。

あるいはPDO::rowCount()メソッドを使ってもよい。この場合はSELECTで何かのカラムを読み込めばよい。

列の全データの取得

検索したい列を抽出し、PDOStatement::fetchAll()で配列として取り出す。以下の例ではprefカラムの全データを取り出し、配列$pref_nameに格納している。

複数列データの連想配列への格納

以下はクエリーの結果を格納する方法の一つ。各レコードをカラム名をキーとした連想配列とし、それを1つの配列に収めている。

例外処理

PDOに例外をthrowさせるには、コンストラクターで以下のようにクラス変数で指定する。

トランザクション

3つのメソッドで簡明に処理できる(PDOによるトランザクションの確認)。

commit()メソッド、rollBack()メソッドはbeginTransaction()が成功していないと例外になるので注意。

beginTransaction()はトランザクションが開始できない場合に例外を投げる。

PDOによるトランザクションの確認はこちらを参照

 

PHP – 雛形 – 入力された内容を残す

概要

普通にHTMLのinput要素などを書くと、GETやPOSTのたびにそれらがvalueなしで描きなおされるので、内容が空になる。これに対してsubmit後に元に入力した値が残るようにする手順。

普通に書くと消える

以下のコードでは、送信ボタンを押すと入力した内容が表示されるが、テキストボックスの内容は毎回消えてしまう。

これはinput要素のvalue属性がないため、POSTのたびに再描画されるときに要素が空として描かれるため。

valueを設定すると値を残せる

input要素のvalue属性に前の入力内容を渡せば、その内容で要素が描かれるため値が残る。

  • POSTされた際にPHPコードの変数は初期状態に戻る(undefinedか初期設定値に戻る)
  • ただしHTMLのinput要素が再描画されるまでは、その要素の内容は残っている
  • そこで、正常に入力が取得された場合にはinput要素のvalue属性の値に入力された値を渡せば、描画時にその値が残される

ただし、GETやPOST後の状態によってvalueにセットする変数がundefinedだとエラーになるため、どのような状態でも変数が定義済みとなるようにしておく必要がある。

以下のコードでは、POSTされた場合はisset()がtrueかfalseかそれぞれの場合に、POSTされずに実行された場合(最初に実行された場合など)にvalueにセットする値を定義している。

エラー処理などで値を残す

GET/POSTされた値の内容によってエラーとする場合にも、エラーとなったその値を残しておける。コード実行の最初にinput要素から取得した値を変数に持っておいて、その内容のままvalue属性の値として渡せばよい(もちろん例外対応は必要)。

以下のコードは、数値入力欄が数値以外の場合エラーとしているが、その入力内容はテキストボックスに残る。

 

PHP – 雛形 – MySQLのクォート

概要

SQLでは一般にシングルクォート(')やダブルクォート(")で文字列を囲う。

テキストの内容としてクォートを含む場合、そのまま記述したりテキストボックスの入力内容をそのまま使ってしまうと文字列境界との区別がつかず、エラーが発生してしまう。

そこでSQLデータの書き込み時に、SQLとして適切に解釈可能なようにエスケープする必要がある。

シングルクォートのエスケープ

SQLエスケープ

SQL標準では、シングルクォートを文字として扱う場合にクォートを2つ連続させる('')

PHPからSQLを操作する際にシングルクォートを含む文字列をリテラルとして扱う場合、以下のような方法がある。

  1. query文字列をダブルクォート(")で囲う
  2. テキストリテラルをシングルクォート(')で囲う
  3. テキストリテラル中のシングルクォートはこれを2回続けて('')SQL標準でエスケープする

定義済み関数によるエスケープ

PHPの文字列として扱う場合、シングルクォートをバックスラッシュでエスケープすることができる(\')。この場合、リテラルの解釈後には1文字のシングルクォートとして扱われる。また、テキストボックスに入力された文字列にクォートが入っているケースも考えられる。

ところで、このような文字列をそのままquery構文に渡してしまうとSQLが文字列境界として扱うので不都合が生じる。そこで、PHPに準備されたエスケープ用の関数を使う。この関数の手続き型の構文は以下の通り。

$escaped_string = mysqli_real_escape_string($dblink, $original_string);

第1引数にmysqli_connect()で得られるデータベースリンクが必要な点に注意。

たとえばターゲットの文字列をPHPの変数として扱う場合、以下の手順が考えられる。

  1. クォートを含む可能性がある文字列をmysqli_real_escape_string()関数でエスケープする
    • リテラルとして文字列を準備する場合は、シングルクォートで囲い、文字列中のシングルクォートをバックスラッシュでエスケープする
  2. queryに文字列変数を渡す際に
    1. 文字列変数の内容がシングルクォートで囲われるようにする
    2. そのため、文字列前後の構文をダブルクォートで囲う

ダブルクォートのエスケープ

ダブルクォートもSQLでは文字列境界として扱われるので、queryの構文中にリテラルで書くとエラーとなる。

テキストボックスなどからの入力の場合はそのままmysqli_real_escape_string()でエスケープするといいが、リテラルの場合は全体をシングルクォートで囲ってやるとよい。

読み込み時

エスケープされた文字列がSQLで保存されると、クォートが1つずつの文字列として記録されている。それらを含むテキストをフェッチして変数に代入すると、適正に表示される。

 

PHP – 雛形 – MySQL操作(mysqli)

概要

PHPからデータベースを操作する手続きの大枠は以下の通り。

  1. DBサーバー・DBに接続してDBリンクを取得
    $db_link =mysql_connect()
    if($db_link)で接続成功/失敗処理

  2. 文字コードを設定する
    mysqli_set_charset()
  3. query文字列によりDBから絞り込んだデータを取得
    mysqli_query()
    →処理内容によって戻り値に注意
  4. DBから1レコードずつ取り出す
    mysqli_fetch_array()/mysqli_fetch_assoc()
  5. レコード内のカラムデータをキーを介して利用
    →カラム名をキーとした連想配列など
  6. SELECTなど結果セットがある場合はメモリーを解放(INSERTの場合は不要)
    mysqli_free_result()
  7. データベースリンクをクローズ
    mysqli_close()

データベースを最初に指定する場合

MySQLに接続後にDBを選択/変更する場合

 

PHP – str_replace~文字列の置換

概要

str_replace()は文字列中の対象文字列を指定した文字列に置き換える。

str_replace($search, $replace, $string);

$searchは置き換えられる文字列、$replaceは置き換える文字列、$stringは対象文字列。

基本形

文字列の変換

以下の例は、対象文字列中の’ab’を’AB’に変換している。

半角スペースの削除

以下の例では、文字列中の半角スペースを全て削除している。trimと異なり、文字列中のすべてのスペースを削除。

全角スペースの削除

全角スペースを削除する例。

全角文字の置換

全角文字も問題なく検索・置換できる。

複数文字列の置換

複数の文字列の同一文字列への置換

第1引数を文字列の配列にすると、各要素文字列の何れかに該当すれば第2引数に置き換えられる。

これを利用して、文字列が半角スペースと全角スペースだけで構成されていないかチェックができる。

if (str_replace([' ', ' '], "") === '') {}

複数の文字列セットの置換

第1引数と第2引数を配列にすると、対応する要素によって置換される。

 

PHP – trim/ltrim/rtrim

概要

trim系の関数は、指定した文字列の先頭・末尾のスペースや区切り文字など以下の文字を取り除く。

  • " " (ASCII 32 (0x20)), 通常の空白
  • "\t" (ASCII 9 (0x09)), タブ
  • "\n" (ASCII 10 (0x0A)), リターン
  • "\r" (ASCII 13 (0x0D)), 改行
  • "\0" (ASCII 0 (0x00)), NULバイト
  • "\v" (ASCII 11 (0x0B)), 垂直タブ

全角のスペースが削除されない、全角文字の場合うまく機能しないなどの制約がある。半角・全角のスペース削除や全角文字列の削除はうまく機能しない。

基本形

trim()は先頭と末尾を、ltrim()は先頭、rtrim()は末尾をトリムする。

  • 先頭/末尾に連続した対象文字は全て削除される
  • 他の文字に挟まれた先頭/末尾以外の対象文字は削除されない

全角スペースは削除されない

指定した文字列でトリム

第2引数でトリムする文字列を指定できる。1文字である必要はない。

全角文字はうまく動作しない

以下の例では、末尾文字列は削除されているが、先頭文字列はうまく削除されていない。

 

PHP – explode~文字列の分解

概要

explode()は指定した区切り文字列で文字列を分解する。

書式

explode ( string $separator , string $string [, int $limit = PHP_INT_MAX ] ) : array

引数

$separator
区切り文字列
$string
区切り文字列で分解される文字列

戻り値

分解された文字列が配列に入れられる。

実行例

','を区切り文字として指定して分解した例。分解後の文字列が空文字列となる場合に注意。

区切りは1文字でなくてもよい。以下の例は'ef'が区切り文字列となり、それを除いて分解された文字列が配列に入れられている。

該当する文字列がない場合は、分解されずに元の文字列を要素とする配列が返される。

区切り文字列が空の場合戻り値がfalseとなるが、警告が発せられる。

 

PHP – 雛形 – ファイル操作

ファイルの確認

file_exists~ファイルの存在確認

基本形

file_exists()は引数で指定したファイル名のファイルが存在するかどうかを確認する。(PHP-manual)

file($filename)

戻り値はファイルの存在/不存在に従ってtrue/false。

is_readable~ファイルの読み込み可能性確認

is_readable()はファイル名を指定したファイルが存在し読み込み可能かどうかを確認する。(PHP-manual)

is_readable($failename)

戻り値は指定したファイルが存在し読み込み可能な場合はtrue、それ以外の場合にfalse

ファイルのオープン/クローズ

fopen~ファイルのオープン

基本形

多くのファイル操作では、最初にファイルをオープンしてファイルポインターリソースを取得する。ファイルの書き込みや読み込みはファイルリソースを通して行う。

fopen()は第1引数にファイル名、第2引数にモードを指定する。モードは書き込み/読み込み、ファイルポインターの位置などに応じて所定の文字列で指定。(PHP-manual)

fopen($filename, $mode)

エラー処理

fopen()の戻り値はresource型だが、オープンに失敗した場合の戻り値はfalse。このため、fopen()のエラーチェックは明示的にfalseであるかどうかを比較する。

flose~ファイルのクローズ

基本形

fopen()得られたファイルポインターリソースを引数に指定してファイルを閉じる。

flose($file)

エラー処理

fclose()の戻り値はbool型で、処理の成功/失敗に応じてtrue/falseが返される。しかし、明示的にfclose()に対するエラーチェックを行うのが一般的なのか(あるいはそうすべきなのか)どうか不明。

行単位の書き込み・読み込み

fwrite~行単位の書き込み

基本形

テキストを行単位で書き込むにはfwrite()を使う。エラー処理を全て除いた基本的な流れは以下の通り。

  1. fopen(ファイル名)でファイルをオープンしてファイルハンドラーを取得
    • モードの指定は新規書き込み('w')、追加書き込み('a')など
    • PHP-manual
  2. fwrite(ファイルハンドラー, 行データ)で行データを順次書き込み
    • 改行文字\nは付けられないので、必要に応じて追加書き込み
    • PHP-manual
  3. flcose(ファイルハンドラー)でファイルをクローズ

上の例では\nを付加しており、出力ファイルの内容は以下のようになる。

エラー処理

fwrite()の戻り値はint型で書き込んだバイト数を返すが、書き込みに失敗した場合の戻り値はfalse。このため、fwrite()のエラーチェックは明示的にfalseであるかどうかを比較する。

fgets~行単位の読み込み

基本形

fgets()\nで区切られた行単位でデータを読み込む。エラー処理を除いた基本的な流れは以下の通り。

  1. fopen(ファイル名)でファイルをオープンしてファイルハンドラーを取得
    • モードの指定は読込('r')など
    • PHP-manual
  2. fgets(ファイルハンドラー)で行データを順次読み込み
    • 通常while文でファイルの最後に行きつくまで順次読み込む
    • PHP-manual
  3. flcose(ファイルハンドラー)でファイルをクローズ

このコードでは、最低限ファイルが存在して読み込み可能なことをis_readble()でチェックしている。

また行データを読み込んだ後、ブラウザーで改行させるため<br>を付加している。その表示結果は以下の通り。

読み込み終了処理

fgets()の戻り値はstring型で読み込んだテキストを返すが、読み込むデータがない場合はfalse。このため、fgets()はのエラーチェックは明示的にfalseであるかどうかを比較する。

一括書き込み・読み込み

file_put_contents

基本形

file_put_contents()は、ファイル名を直接指定してデータを一括して書き込む。(PHP-manual)

この例では配列を直接渡して全要素を書き出しているが、要素間に改行文字は挿入されず、連続して書き込まれる。

要素ごとに行を区切る場合には、foreachで要素を取り出して改行文字\nを付加して書き込む。ただしこのときは前のデータを上書きしないようにFILE_APPENDフラグを立てる必要があるが、そうすると既にファイルが存在している際に追加書き込みになってしまう。

ファイルが存在しない(あるいは空のファイルが存在する)場合のファイルの内容は以下のようになる。

エラー処理

file_put_contents()の戻り値はint型で書き込んだバイト数を返すが、書き込みに失敗した場合の戻り値はfalse。このため、file_put_contents()のエラーチェックは明示的にfalseであるかどうかを比較する。

file_get_contents

基本形

file_get_contents()はファイル名を指定してその内容を一つの変数に読み込む。(PHP-manual)

改行文字\nもそのまま読み込まれるが、ブラウザーでは反映されないため、上のコードの表示結果は以下のようになる。

一括取得されたテキストをブラウザー上で改行させたい場合には、以下のように\n<br>に置き換えるとよい。

エラー処理

file_get_contents()の戻り値はstring型で読み込んだデータを返すが、読み込みに失敗した場合の戻り値はfalse。このため、file_get_contents()のエラーチェックは明示的にfalseであるかどうかを比較する。

readfile

基本形

readfile()はファイルの内容を一括して読み込み、それを直接表示する。(PHP-manual)

改行文字はブラウザーでは反映されないため、行単位で区切られたファイルでも表示は以下のようになる。

エラー処理

readfile()の戻り値はint型で読み込んだデータのバイト数を返すが、読み込みに失敗した場合の戻り値はfalse。このため、file_get_contents()のエラーチェックは明示的にfalseであるかどうかを比較する。

CSVファイルの書き込み・読み込み

fputcsv

基本形

fputcsv()は配列データをCSVファイルとして書き込む。基本の流れはfwrite()と同じ。(PHP-mannual)

上記のコードで配列をCSVとして書き込んだファイルの内容は以下の通り。

エラー処理

fputcsv()の戻り値はint型で書き込んだ文字数を返すが、書き込みに失敗した場合の戻り値はfalse。このため、file_put_contents()のエラーチェックは明示的にfalseであるかどうかを比較する。

fgetcsv

基本形

fgetcsv()はCSVからデータを読み込む。読み込みは行ごとに行われ、1つの行(フィールドは)カンマ(あるいは指定した文字)で区切られたデータごとに分けられ、1次元の配列として返される。(PHP-manual)

上記コードの表示結果は以下の通り。

エラー処理

rgetcsv()の戻り値は読み込んだ行のデータの数値展示配列だが、無効なハンドルを受け取った場合はnull、ファイル終端に達したときやその他のエラーの場合はfalseを返す。このため、fgetcsv()のエラーチェックは明示的にfalseであるかどうかを比較する。