PHPからPostgreSQLに接続する方法について

採用はこちら

PHPからPostgreSQLへ接続する方法には、主に「PDOを利用する方法」と「PostgreSQL専用のpgsql拡張を利用する方法」があります。

一般的なWebアプリケーションでは、データベースへの接続やSQLの実行、プリペアドステートメント、トランザクションなどを共通した書き方で扱えるPDOが便利です。

一方、PostgreSQL固有の機能を積極的に利用したい場合には、pg_connect()などを備えたpgsql拡張を利用する方法もあります。

この記事では、PHPからPostgreSQLへ接続する基本的な方法から、データの取得・追加・更新・削除、エラー処理、セキュリティ対策まで詳しく解説します。

目次

PHPからPostgreSQLへ接続する代表的な方法

PHPからPostgreSQLへ接続する方法は、大きく分けると次の2種類です。

  • PDO_PGSQLを使う方法
  • pgsql拡張を使う方法

どちらを利用してもPostgreSQLへ接続できますが、特徴が異なります。

PDO_PGSQLを使う方法

PDOは、PHPからさまざまなデータベースへアクセスするための共通インターフェイスです。

PostgreSQLへPDOで接続する場合は、PostgreSQL用のPDOドライバであるPDO_PGSQLが必要です。

PDOを利用すると、MySQLやSQLiteなど他のデータベースでも比較的似た書き方で処理できます。

データベースを変更する可能性がある場合や、統一的な書き方を採用したい場合に便利です。

pgsql拡張を使う方法

PHPにはPostgreSQL専用のpgsql拡張も用意されています。

pgsql拡張では、pg_connect()pg_query_params()など、PostgreSQL専用の関数を利用できます。

PostgreSQL固有の機能を利用する場合には、pgsql拡張が適しているケースもあります。

PDOが必ず優れているわけではないため、アプリケーションの要件に応じて選択することが大切です。

PDO_PGSQLが利用できるか確認する

PDOを利用してPostgreSQLへ接続するには、PHPでPDO_PGSQLが有効になっている必要があります。

php -mで確認する

コマンドラインから、次のコマンドを実行します。

php -m

一覧の中に次のような項目が表示されているか確認します。

PDO
pdo_pgsql

pdo_pgsqlが表示されていれば、PDOを利用してPostgreSQLへ接続できます。

PDO::getAvailableDrivers()で確認する

PHPプログラムから利用可能なPDOドライバを確認することもできます。

<?php

print_r(PDO::getAvailableDrivers());

PostgreSQL用のPDOドライバが利用できる場合は、次のようにpgsqlが表示されます。

Array
(
    [0] => mysql
    [1] => pgsql
    [2] => sqlite
)

PDO本体だけではPostgreSQLへ接続できません。

PostgreSQLへ接続する場合は、PDO_PGSQLのようなデータベース固有のPDOドライバが必要です。

PDOを使ってPostgreSQLへ接続する

PDOを利用した基本的な接続コードは次のとおりです。

<?php

$host = 'localhost';
$port = '5432';
$dbname = 'sample_db';
$user = 'app_user';
$password = 'password';

$dsn = "pgsql:host={$host};port={$port};dbname={$dbname}";

try {
    $pdo = new PDO(
        $dsn,
        $user,
        $password,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]
    );

    echo 'PostgreSQLへの接続に成功しました。';
} catch (PDOException $e) {
    echo 'データベースへの接続に失敗しました。';
}

new PDO()を実行すると、指定した接続情報を使ってPostgreSQLへの接続が行われます。

DSNの書き方

PostgreSQLへPDOで接続するときは、DSNを次のように指定します。

pgsql:host=ホスト名;port=ポート番号;dbname=データベース名

たとえば、次のように記述できます。

$dsn = 'pgsql:host=localhost;port=5432;dbname=sample_db';

pgsql:はPostgreSQL用のPDOドライバを使用することを意味します。

hostの意味

hostには、PostgreSQLサーバーのホスト名やIPアドレスを指定します。

PHPとPostgreSQLが同じ環境で動作している場合には、次のように指定することがあります。

$host = 'localhost';

ただし、Dockerを利用してPHPとPostgreSQLを別コンテナで動かしている場合、localhostでは接続できないケースがあります。

Docker Composeなどでは、PostgreSQLのサービス名を指定することがあります。

たとえば、PostgreSQLのサービス名がdbであれば、次のように指定します。

$host = 'db';

portの意味

portにはPostgreSQLが待ち受けているポート番号を指定します。

PostgreSQLのデフォルトポートは一般的に5432です。

$port = '5432';

ただし、サーバー側の設定によって変更されている場合があります。

実際のPostgreSQL環境に合わせて指定してください。

dbnameの意味

dbnameには接続するデータベース名を指定します。

$dbname = 'sample_db';

指定したデータベースがPostgreSQL上に存在している必要があります。

userとpasswordの意味

userpasswordには、PostgreSQLへログインするためのユーザー名とパスワードを指定します。

$user = 'app_user';
$password = 'password';

本番環境では、管理者権限を持つユーザーをそのままWebアプリケーションで利用するのではなく、アプリケーション専用のユーザーを作成することが推奨されます。

PDO::ERRMODE_EXCEPTIONについて

PDOでは、エラーの扱い方をPDO::ATTR_ERRMODEで設定できます。

次の設定を行うと、データベース処理でエラーが発生した場合に例外が発生します。

PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

PHP 8.0以降では例外モードがデフォルト

PHP 8.0以降では、PDO::ERRMODE_EXCEPTIONがPDOのデフォルトのエラーモードです。

そのため、PHP 8以降では明示的に指定しなくても、PDOエラーは基本的に例外として処理されます。

ただし、コードの意図を明確にするために、次のように明示的に設定しても問題ありません。

$pdo = new PDO(
    $dsn,
    $user,
    $password,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]
);

設定内容がコードを見ただけで分かるため、保守性の面でも有効です。

PostgreSQLからデータを取得する

PostgreSQLへの接続が完了したら、SQLを実行してデータを取得できます。

SELECT文を実行する

たとえば、usersテーブルからユーザー情報を取得する場合は次のように記述します。

<?php

$stmt = $pdo->query(
    'SELECT id, name, email FROM users'
);

$users = $stmt->fetchAll();

foreach ($users as $user) {
    echo $user['name'] . PHP_EOL;
}

PDO::ATTR_DEFAULT_FETCH_MODEPDO::FETCH_ASSOCを指定している場合、取得したデータはカラム名をキーとする連想配列として扱えます。

fetch()で1件取得する

1件だけ取得したい場合は、fetch()を利用できます。

$stmt = $pdo->query(
    'SELECT id, name, email FROM users LIMIT 1'
);

$user = $stmt->fetch();

if ($user !== false) {
    echo $user['name'];
}

データが取得できなかった場合、fetch()falseを返します。

WHERE条件ではプリペアドステートメントを使う

ユーザーから受け取った値などをSQLへ渡す場合は、文字列を直接連結するのではなく、プリペアドステートメントを利用することが重要です。

prepare()とexecute()を使う

たとえば、ユーザーIDを指定してデータを取得する場合は次のように記述します。

<?php

$id = 10;

$stmt = $pdo->prepare(
    'SELECT id, name, email FROM users WHERE id = :id'
);

$stmt->execute([
    ':id' => $id,
]);

$user = $stmt->fetch();

if ($user !== false) {
    echo $user['name'];
}

SQLと値を分離することで、SQLインジェクション対策につながります。

値をSQLへ直接連結しない

次のようなコードは避けるべきです。

$sql = "SELECT * FROM users WHERE id = " . $id;

外部から受け取った値をそのままSQLへ組み込むと、不正なSQLが実行される危険性があります。

プリペアドステートメントを利用し、値をパラメータとして渡しましょう。

プレースホルダーにはテーブル名やカラム名を指定できない

PDOのプレースホルダーを利用する際には、注意点があります。

プレースホルダーで指定できるのは基本的に「値」です。

たとえば、次の使い方は問題ありません。

SELECT * FROM users WHERE id = :id

しかし、次のような使い方はできません。

SELECT * FROM :table

テーブル名を:tableへバインドすることはできません。

同様に、次のような記述も適切ではありません。

ORDER BY :column

カラム名やテーブル名、ASCDESCなどSQL構造を動的に変更する場合は、許可する値をあらかじめ決めたホワイトリスト方式で検証する必要があります。

PostgreSQLへデータを追加する

データを追加する場合は、INSERT文を利用します。

INSERT文の例

<?php

$name = 'Yamada';
$email = 'yamada@example.com';

$stmt = $pdo->prepare(
    'INSERT INTO users (name, email)
     VALUES (:name, :email)'
);

$stmt->execute([
    ':name' => $name,
    ':email' => $email,
]);

echo '登録しました。';

外部入力を利用する場合は、INSERT文でもプリペアドステートメントを使うことが重要です。

PostgreSQLのデータを更新する

データを更新する場合は、UPDATE文を利用します。

UPDATE文の例

<?php

$id = 10;
$name = 'Suzuki';

$stmt = $pdo->prepare(
    'UPDATE users
     SET name = :name
     WHERE id = :id'
);

$stmt->execute([
    ':name' => $name,
    ':id' => $id,
]);

UPDATE文では、WHERE条件を付け忘れると複数のレコードが一括で更新される可能性があります。

実行する前に条件を十分確認しましょう。

PostgreSQLのデータを削除する

データを削除する場合は、DELETE文を利用します。

DELETE文の例

<?php

$id = 10;

$stmt = $pdo->prepare(
    'DELETE FROM users WHERE id = :id'
);

$stmt->execute([
    ':id' => $id,
]);

DELETE文もWHERE条件の指定を誤ると大量のデータを削除してしまう可能性があります。

特に本番環境では慎重に実行する必要があります。

pg_connect()を使ってPostgreSQLへ接続する

PDOではなく、PostgreSQL専用のpgsql拡張を使って接続する方法もあります。

pg_connect()の基本的な使い方

<?php

$connectionString =
    'host=localhost ' .
    'port=5432 ' .
    'dbname=sample_db ' .
    'user=app_user ' .
    'password=password';

$conn = pg_connect($connectionString);

if ($conn === false) {
    echo 'PostgreSQLへの接続に失敗しました。';
    exit;
}

echo 'PostgreSQLへの接続に成功しました。';

pg_connect()では、接続情報を文字列として指定します。

pg_query_params()でSQLを実行する

pgsql拡張を利用する場合、外部から受け取った値をSQLに渡すときはpg_query_params()を利用できます。

pg_query_params()の基本例

<?php

$conn = pg_connect(
    'host=localhost port=5432 dbname=sample_db user=app_user password=password'
);

$id = 10;

$result = pg_query_params(
    $conn,
    'SELECT id, name, email FROM users WHERE id = $1',
    [$id]
);

while ($row = pg_fetch_assoc($result)) {
    echo $row['name'];
}

pg_query_params()では、パラメータを次のように指定します。

$1
$2
$3

たとえば、複数の値を渡す場合は次のようになります。

$result = pg_query_params(
    $conn,
    'SELECT * FROM users WHERE name = $1 AND age >= $2',
    [$name, $age]
);

PDOの:id:nameとは記法が異なる点に注意しましょう。

pg_query()よりpg_query_params()を利用する

固定されたSQLを実行するだけであれば、pg_query()を利用できます。

$result = pg_query(
    $conn,
    'SELECT * FROM users'
);

しかし、SQLへ変数を渡す場合は、値を安全に分離できるpg_query_params()を利用するほうが適しています。

ユーザー入力を直接SQL文字列へ連結する書き方は避けましょう。

PDOでトランザクションを利用する

複数のSQLをひとまとまりとして処理したい場合には、トランザクションを利用します。

トランザクションの基本例

<?php

try {
    $pdo->beginTransaction();

    $stmt1 = $pdo->prepare(
        'UPDATE accounts
         SET balance = balance - :amount
         WHERE id = :id'
    );

    $stmt1->execute([
        ':amount' => 1000,
        ':id' => 1,
    ]);

    $stmt2 = $pdo->prepare(
        'UPDATE accounts
         SET balance = balance + :amount
         WHERE id = :id'
    );

    $stmt2->execute([
        ':amount' => 1000,
        ':id' => 2,
    ]);

    $pdo->commit();
} catch (Throwable $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }

    throw $e;
}

すべての処理が成功した場合はcommit()を実行します。

途中でエラーが発生した場合はrollBack()を実行し、処理を取り消します。

送金処理や注文処理など、複数のSQLを同時に成功させる必要がある場面で有効です。

PostgreSQLの接続エラーを処理する

データベースへの接続では、パスワードの間違いやサーバー停止などによってエラーが発生することがあります。

tryとcatchを利用する

PDOでは、次のように例外を処理できます。

try {
    $pdo = new PDO(
        $dsn,
        $user,
        $password,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]
    );
} catch (PDOException $e) {
    echo 'データベースに接続できませんでした。';
}

本番環境で詳細なエラーを表示しない

開発中には、次のようにエラー内容を確認したくなることがあります。

echo $e->getMessage();

しかし、本番環境で詳細なデータベースエラーをそのまま画面へ表示することは避けるべきです。

SQL、テーブル名、ホスト名などの内部情報が利用者へ公開される可能性があるためです。

本番環境では、次のようにログへ詳細を記録する方法があります。

catch (PDOException $e) {
    error_log($e->getMessage());

    echo 'データベース処理中にエラーが発生しました。';
}

ユーザーには一般的なエラーメッセージだけを表示し、詳細はサーバー側で確認できるようにすると安全です。

PostgreSQLの接続情報をソースコードへ直接書かない

サンプルコードでは分かりやすくするため、次のようにパスワードを記述しています。

$password = 'password';

本番環境では、接続情報をソースコードへ直接記述することはできるだけ避けましょう。

環境変数から接続情報を取得する

たとえば、環境変数を利用して次のように取得できます。

<?php

$host = getenv('DB_HOST');
$port = getenv('DB_PORT');
$dbname = getenv('DB_NAME');
$user = getenv('DB_USER');
$password = getenv('DB_PASSWORD');

$dsn = "pgsql:host={$host};port={$port};dbname={$dbname}";

$pdo = new PDO(
    $dsn,
    $user,
    $password,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
);

環境変数には次のような情報を設定します。

DB_HOST=localhost
DB_PORT=5432
DB_NAME=sample_db
DB_USER=app_user
DB_PASSWORD=********

重要なのは、パスワードなどの機密情報をGitリポジトリなどへ誤って登録しないことです。

環境変数だけでなく、クラウドサービスが提供するシークレット管理機能を利用する方法もあります。

PostgreSQLでは専用ユーザーを利用する

WebアプリケーションからPostgreSQLへ接続する場合は、アプリケーション専用のデータベースユーザーを作成することが重要です。

管理者ユーザーを常用しない

たとえば、強い権限を持つ管理者ユーザーをWebアプリケーションから直接利用すると、アプリケーション側で問題が発生した場合に被害が大きくなる可能性があります。

そのため、次のような専用ユーザーを作成します。

app_user

そして、アプリケーションに必要なテーブルや操作だけを許可します。

必要最小限の権限を与えることで、セキュリティリスクを抑えられます。

SSL・TLSを利用してPostgreSQLへ接続する

PHPとPostgreSQLが別のサーバーで動作している場合には、SSL・TLSによる通信の暗号化も検討する必要があります。

特にクラウド上のPostgreSQLへ接続する場合は、サービス側が指定するSSL設定に従いましょう。

sslmodeを利用する

PostgreSQLでは、接続時にsslmodeを指定できます。

代表的な設定には次のものがあります。

disable
allow
prefer
require
verify-ca
verify-full

verify-caでは、サーバー証明書が信頼できる認証局によって発行されているかを検証します。

verify-fullでは、証明書の検証に加えて、接続先のホスト名が証明書と一致しているかも確認します。

セキュリティを重視する場合は、環境に応じて適切な証明書検証を行うことが重要です。

具体的な設定方法は、利用しているPostgreSQLサーバーやクラウドサービスの公式ドキュメントに従ってください。

PHPからPostgreSQLへ接続できない場合の確認ポイント

PHPからPostgreSQLへ接続できない場合は、原因を順番に確認しましょう。

PHP側を確認する

まず、PHP側で必要な拡張機能が有効になっているか確認します。

PDOを使う場合は、次の項目を確認します。

PDO
pdo_pgsql

pgsql拡張を利用する場合は、pgsqlが有効になっているか確認します。

接続情報を確認する

次の接続情報に間違いがないか確認しましょう。

  • ホスト名
  • ポート番号
  • データベース名
  • ユーザー名
  • パスワード

1文字でも間違っていると接続できません。

PostgreSQLが起動しているか確認する

PostgreSQLサーバー自体が停止していれば、PHPから接続できません。

PostgreSQLのサービスが正常に起動しているか確認します。

接続権限を確認する

PostgreSQL側で、対象ユーザーがデータベースへ接続できる権限を持っているか確認します。

また、対象テーブルへのSELECTやINSERTなどの権限も必要です。

ネットワーク設定を確認する

PHPとPostgreSQLが別のサーバーに存在する場合は、ネットワーク設定も確認する必要があります。

ファイアウォールやクラウドのセキュリティ設定によって、PostgreSQLへの通信が遮断されている場合があります。

Docker環境の場合は、コンテナ間のネットワーク設定も確認しましょう。

PostgreSQLへの接続確認用コード

最初に接続できるかだけを確認したい場合は、簡単なSQLを実行すると分かりやすいです。

CURRENT_TIMESTAMPを取得する

<?php

$dsn = 'pgsql:host=localhost;port=5432;dbname=sample_db';
$user = 'app_user';
$password = 'your_password';

try {
    $pdo = new PDO(
        $dsn,
        $user,
        $password,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]
    );

    $stmt = $pdo->query('SELECT CURRENT_TIMESTAMP');

    $result = $stmt->fetch();

    echo '接続成功';
    echo '<br>';
    echo $result['current_timestamp'];
} catch (PDOException $e) {
    echo 'データベースへの接続に失敗しました。';
}

CURRENT_TIMESTAMPを取得できれば、PHPからPostgreSQLへ接続し、SQLを実行して結果を取得できる状態であることを確認できます。

固定されたSQLを実行するだけなので、このようなケースではquery()を利用しても問題ありません。

PHPからPostgreSQLへ接続するときのポイント

PHPからPostgreSQLへ接続する場合は、PDO_PGSQLを利用する方法と、pgsql拡張を利用する方法があります。

一般的なWebアプリケーションでは、接続やSQL実行、プリペアドステートメント、トランザクションを統一的に扱えるPDOが便利です。

一方、PostgreSQL固有の機能を利用したい場合は、pg_connect()pg_query_params()などを利用できるpgsql拡張も選択肢になります。

実際にWebアプリケーションを構築する場合は、単に接続できるだけでなく、セキュリティ面にも注意することが重要です。

特に、外部から受け取った値をSQL文字列へ直接連結せず、プリペアドステートメントやpg_query_params()を利用しましょう。

また、データベースのパスワードをソースコードへ直接記述せず、環境変数やシークレット管理機能などを利用して認証情報を分離することも大切です。

さらに、PostgreSQLの接続ユーザーには必要最小限の権限だけを与え、リモート接続では必要に応じてSSL・TLSによる通信の保護を行いましょう。

これらを意識することで、PHPからPostgreSQLへ安全かつ保守しやすい形で接続できるようになります。

以上、PHPからPostgreSQLに接続する方法についてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次