PHP/データベースとの連動

< PHP

PHPは、複数のRDBMSとの連携機能を持ちます。 連携の方法として

  • ベンダー固有モジュール
  • 抽象化レイヤー

の2つがあります。

ベンダー固有モジュール
ベンダーが提供する機能やデータ構造をPHPに直喩的にマッピングする傾向があり、意味論の差が小さいがゆえに対象のRDBMSの機能を十分発揮できると期待できます。
一方、ベンダー固有機能に依存することになるので、他のRDBMSへの以降は困難ないし書き換えになるリスクがあります。
抽象化レイヤー
DBA/ODBC/PDOの3種類があります。
RDBMSの共通機能を抽出し機能とデータ構造を抽象化する試みで、抽象クラスやインターファースに対応する概念です。
現在存在するRDBMS間の移行時の手直し最小化するとともに、将来現れる新しいRDBMSへのマイグレーションも容易にできると期待できます。

SQLite編集

PHP5.4以降のPHPには、標準でSQLiteへのインタフェースが付属しています。

SQLiteは、以下のような学習用途に適した特徴を持っています。

SQLiteの特徴としては、

  • MySQLのようなサーバークライアントモデルではなく、アプリケーションプログラムにライブラリーとしてリンクされます。
    MySQLようにRDBMSのサーバーのセットアップが不要になり。ポートの開放・認証情報のセットアップや各種パーミッションの整合性の維持が不要です。
  • MySQLは強い型付けですが、SQLiteにはデーター型について自動変換が行われる弱い型付けです(個々のフィールドに制約をかけることはできます)。

sqlite3 が、SQLite3のデーターベースの保守管理を行うコマンドラインインターフェースです。 sqlite3 が、アプリケーションプログラムがリンクされるわけではありません。 sqlite3 自身も、SQLite3がリンクされた、アプリケーションプログラムの1つだと言えます。

ベンダー固有モジュール 編集

SQLite3編集

PHPは、SQLite3クラスでSQLite をサポートしています。

<?php
// SQLite3クラスを使用したデーターベースハンドリング
header("Content-Type: text/plain");

$prefs = [
    [01, "北海道", "Hokkaido"],
    [02, "青森県", "Aomori"],
    [03, "岩手県", "Iwate"],
];
$db = new SQLite3(":memory:");
$db->exec(
    "CREATE TABLE 都道府県コード一覧表 (都道府県コード INTEGER, 都道府県 STRING, prefectures STRING)"
);

try {
    $db->enableExceptions(true);

    $insert = $db->prepare(
        "INSERT INTO 都道府県コード一覧表 VALUES (:n,:ja,:en)"
    );
    foreach ($prefs as $pref) {
        $insert->bindParam(":n", $pref[0]);
        $insert->bindParam(":ja", $pref[1]);
        $insert->bindParam(":en", $pref[2]);
        $insert->execute();
    }
} catch (Exception $e) {
    echo sprintf("%s(%d): %s", $e->getFile(), $e->getLine(), $e->getMessage()), PHP_EOL;
}
$insert = null;

$query = "SELECT * FROM 都道府県コード一覧表;";
echo "$query", PHP_EOL;
$result = $db->query($query);
while ($assoc = $result->fetchArray(SQLITE3_ASSOC)) {
    array_walk($assoc, function (&$v, $k) {
        $v = "$k:$v";
    });
    $v = null;
    echo implode(",", $assoc), PHP_EOL;
}
echo PHP_EOL;
実行結果
SELECT * FROM 都道府県コード一覧表;
都道府県コード:1, 都道府県:北海道, prefectures:Hokkaido
都道府県コード:2, 都道府県:青森県, prefectures:Aomori
都道府県コード:3, 都道府県:岩手県, prefectures:Iwate
機能的には不足はないのですが、次に示すPDOバージョンの方が、それを元に他のRDBMSにも対応できるので、より汎用的です。
SQLite3クラスとPDOを比較してみてください。

抽象化レイヤー編集

PDO編集

PDO(PHP Data Object) は、PHPに拡張モジュールとして標準で提供されているデーターベース抽象化レイヤーの1つです。 各種データベースへの接続を抽象化して、ユーザーコードからDBMSの違いを隠蔽します。 PDOは、オブジェクト指向のAPIを提供します(手続き指向のAPIはありません)。

<?php
// PDOを使用したデーターベースハンドリング
header('Content-Type: text/plain');

$prefs = [
    [01, "北海道", "Hokkaido"],
    [02, "青森県", "Aomori"],
    [03, "岩手県", "Iwate"],
];
$db = new PDO("sqlite::memory:");
$db->exec(
    "CREATE TABLE 都道府県コード一覧表 (都道府県コード INTEGER, 都道府県 STRING, prefectures STRING)"
);
$insert = $db->prepare("INSERT INTO 都道府県コード一覧表 VALUES (:n,:ja,:en)");
$db->beginTransaction();

try {
    foreach ($prefs as $pref) {
        $insert->bindParam(":n", $pref[0]);
        $insert->bindParam(":ja", $pref[1]);
        $insert->bindParam(":en", $pref[2]);
        $insert->execute();
    }
    $db->commit();
} catch (PDOException $e) {
    echo $e;
    $db->rollback();
    throw $e;
}
$insert = null;

foreach (
    $db->query("SELECT * FROM 都道府県コード一覧表")->fetchAll(PDO::FETCH_ASSOC)
    as $assoc
) {
    echo implode(
        ", ",
        array_map(
            fn($k, $v) => "$k:$v",
            array_keys($assoc),
            array_values($assoc)
        )
    );
    echo PHP_EOL;
}

?>
実行結果
都道府県コード:1, 都道府県:北海道, prefectures:Hokkaido
都道府県コード:2, 都道府県:青森県, prefectures:Aomori 
都道府県コード:3, 都道府県:岩手県, prefectures:Iwate
PDOを使い抽象化することで、SQLiteとMySQLの違いを吸収しています。
queryメソッドではなく、prepareメソッドを使っているのは名前付きプレースホルダーを使いたかったためで、これはSQLインジェクションへの配慮です。

ウェブレンダリング編集

同内容で、HTMLに出力してみましょう。

<?php
// PDOを使用したデーターベースハンドリング
// phpinfo();
declare(strict_types=1);
header('Content-Type: text/html');

$prefs = [
    [01, "北海道", "Hokkaido"],
    [02, "青森県", "Aomori"],
    [03, "岩手県", "Iwate"],
];
$db = new PDO("sqlite::memory:");
$db->exec(
    "CREATE TABLE 都道府県コード一覧表 (都道府県コード INTEGER, 都道府県 STRING, prefectures STRING)"
);
$insert = $db->prepare("INSERT INTO 都道府県コード一覧表 VALUES (:n,:ja,:en)");
$db->beginTransaction();

try {
    foreach ($prefs as $pref) {
        $insert->bindParam(":n", $pref[0]);
        $insert->bindParam(":ja", $pref[1]);
        $insert->bindParam(":en", $pref[2]);
        $insert->execute();
    }
    $db->commit();
} catch (PDOException $e) {
    echo $e;
    $db->rollback();
    throw $e;
}
$insert = null;
?>
<table class=wikitable>
    <caption>都道府県コード一覧表</caption>
    <tr><?php foreach ($db->query("PRAGMA table_info('都道府県コード一覧表')")->fetchAll(PDO::FETCH_ASSOC) as $assoc) : ?><th><?= $assoc['name'] ?><?php endforeach ?></tr>
<?php foreach ($db->query("SELECT * FROM 都道府県コード一覧表")->fetchAll(PDO::FETCH_ASSOC) as $assoc) : ?>
    <tr><?php foreach (array_values($assoc) as $value) { ?><td><?= $value ?><?php } ?></tr>
<?php endforeach ?>
</table>
実行結果
<table class=wikitable>
    <caption>都道府県コード一覧表</caption>
    <tr><th>都道府県コード<th>都道府県<th>prefectures</tr>
    <tr><td>1<td>北海道<td>Hokkaido</tr>
    <tr><td>2<td>青森県<td>Aomori</tr>
    <tr><td>3<td>岩手県<td>Iwate</tr> 
</table>
レンダリング結果
都道府県コード一覧表
都道府県コード都道府県prefectures
1北海道Hokkaido
2青森県Aomori
3岩手県Iwate
この例では、見出し語をそのままテーブルのフィールド名にしているのでスキーマーの情報をそのままレンダリングに使っていますが、フィールド名を英数字に限定している場合は、フィールド名⇒見出し語のテーブルを作り、その情報でイテレーションすると良いでしょう。

MySQL編集

PHPとMySQLを連携するには、MySQLのインストールとセットアップが必要です。

ベンダー固有モジュール編集

MySQLi編集

MySQLiは、以前のMySQLを強化しMySQL 4.1 以上で提供される機能を利用できるようにした拡張モジュールです。 MySQLiは、拡張モジュールなのでPHP本体とは別にインストールする必要があります。 オペレーションシステム/ディストリビューションのパッケージ管理システムでパッケージの検索・インストールを行った後にセットアップを行います。

MySQLには、オブジェクト指向のAPIと手続き指向のAPIの2つのAPIがあります。

CLIからのデーターベース操作(オブジェクト指向)編集
<?php
$mysqli = new mysqli("localhost", "root", "", "testmysql");

foreach (
    $mysqli->query("SELECT * FROM testTable")->fetch_array(MYSQLI_ASSOC)
    as $key => $value
) {
    echo "$key:$value", PHP_EOL;
}
$mysqli->close();
$mysqli = null;
?>
実行結果
原子番号:1
元素名:Hydrogen
元素記号:H
全レコードの読取り
<?php
$mysqli = new mysqli("localhost", "root", "", "testmysql");

foreach (
    $mysqli->query("SELECT * FROM testTable")->fetch_all(MYSQLI_ASSOC)
    as $assoc
) {
    foreach ($assoc as $key => $value) {
        echo "$key:$value", PHP_EOL;
    }
}
$mysqli->close();
$mysqli = null;
?>
実行結果
原子番号:1
元素名:Hydrogen
元素記号:H
原子番号:2
元素名:Helium
元素記号:
CLIからのデーターベース操作(手続き指向)編集
<?php
$connect = mysqli_connect("localhost", "root", "", "testmysql");
$result  = mysqli_query($connect, "SELECT * FROM testTable");

foreach (mysqli_fetch_array($result, MYSQLI_ASSOC) as $key => $value) {
    echo "$key:$value", PHP_EOL;
}
?>
実行結果
原子番号:1
元素名:Hydrogen
元素記号:H
オブジェクト指向版に比べて、手続き指向版のほうがメソッドチェインを使えない関係で中間変数が増えコードが長くなる傾向にあります。
全レコードの読取り
<?php
$connect = mysqli_connect("localhost", "root", "", "testmysql");
$result  = mysqli_query($connect, "SELECT * FROM testTable");

foreach (mysqli_fetch_all($result, MYSQLI_ASSOC) as $assoc) {
    foreach ($assoc as $key => $value) {
        echo "$key:$value", PHP_EOL;
    }
}
?>
実行結果
原子番号:1
元素名:Hydrogen
元素記号:H
原子番号:2
元素名:Helium
元素記号:

抽象化レイヤー編集

PDO編集

<?php
$pdo = new PDO(
    "mysql:host=localhost;dbname=testmysql;charset=utf8",
    "root",
    ""
);
foreach (
    $pdo->query("SELECT * FROM testTable")->fetchAll(PDO::FETCH_ASSOC)
    as $assoc
) {
    foreach ($assoc as $key => $value) {
        echo "$key:$value", PHP_EOL;
    }
}
実行結果
原子番号:1
元素名:Hydrogen
元素記号:H
原子番号:2
元素名:Helium
元素記号:
ベンダー固有モジュール版のMySQLiには、手続き指向版のAPIがありましたが、PDOにはオブジェクト指向版しかありません。