質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.86%

PHPのトランザクションエラーの原因がわかりません。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,676

dog57

score 116

PHPで編集機能を作成していたところ、トランザクションエラーが発生しましたが、
原因が特定できません。

一体何が原因なのでしょうか?

エラー

Fatal error: Uncaught PDOException: There is no active transaction in /var/www/html/Portfolio/public_html/account_edit_insert.php:163 Stack trace: #0 /var/www/html/Portfolio/public_html/account_edit_insert.php(163): PDO->rollBack() #1 {main} thrown in /var/www/html/Portfolio/public_html/account_edit_insert.php on line 163

account_edit_insert.php

<?php
ini_set("display_errors", 1);
error_reporting(E_ALL);

session_start();

// 文字化け対策
header("Content-type: text/html; charset=UTF-8");

require_once('./functions.php');
require_once('../config/db.php');

// ログインしていなければ login_form.php に遷移
require_logined_session();


// 初期化
$errors = array();


// POST時
if (filter_input(INPUT_SERVER, 'REQUEST_METHOD') == 'POST') {


    $_SESSION["name"] = filter_input(INPUT_POST, 'name');
    $_SESSION["email"] = filter_input(INPUT_POST, 'email');
    $_SESSION["password"] = filter_input(INPUT_POST, 'password');
    $_SESSION["tel"] = filter_input(INPUT_POST, 'tel');

    $name = $_SESSION["name"];
    $email = $_SESSION["email"];
    $password = $_SESSION["password"];
    $tel = $_SESSION["tel"];


    //
    // バリデーション
    //

    // 入力チェック
    if (empty($_SESSION["name"])) {

        $errors[] = "名前が入力されていません。";

    }

    if (empty($_SESSION["email"])) {

        $errors[] = "E-mailが入力されていません。";

    }

    if (empty($_SESSION["password"])) {

        $errors[] = "パスワードが入力されていません。";

    }

    if (empty($_SESSION["tel"])) {

        $errors[] = "電話番号が入力されていません。";

    }


    // 名前の文字数チェック
    if (strlen($_SESSION["name"]) >= 60) {

        $errors[] = "氏名が長すぎます。";

    }

    // パスワード文字数チェック(8文字以上かどうか)
    if (preg_match("/^[a-zA-Z1-9]{1,7}$/", $_SESSION['password'])) {

        $errors[] = "パスワードは8文字以上で入力してください。";

    }

    // 電話番号の文字数チェック(10文字 or 11文字)
    if (strlen($_SESSION['tel']) >= 1 && strlen($_SESSION['tel']) <= 9 && preg_match("/^[0-9]+$/", $_SESSION['tel'])) {

        $errors[] = "電話番号は10文字か11文字で入力してください。";

    } elseif (strlen($_SESSION['tel']) >= 12 && preg_match("/^[0-9]+$/", $_SESSION['tel'])) {

        $errors[] = "電話番号は10文字か11文字で入力してください。";

    }

    // メールアドレス形式チェック
    if (!preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $_SESSION['email']) && $_SESSION['email'] !== '') {

        $errors[] = "メールアドレスに間違いがあります。";

    }

    // 電話番号の形式チェック
    if (preg_match("/[-]+/", $_SESSION['tel'])) {

        $errors[] = "電話番号はハイフンなしで入力してください。";

    }

    // 電番番号の数字チェック
    // メールアドレス形式チェック
    if (!preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $_SESSION["email"]) && $_SESSION["email"] !== "") {

        $errorMsg[] = "メールアドレスに間違いがあります。";

    }





if (count($errors) > 0) {

    $_SESSION["errors"] = $errors;
    header("Location: account_edit.php");

}






if (count($errors) === 0) {

    try {

        $dbh = new PDO($dsn, $user, $password);

        // プリペアドステートメント
        $statement = $dbh->prepare("UPDATE users SET name = :name, email = :email, password = :password, tel = :tel
        WHERE id = :id");

        // トランザクション開始
        $dbh->beginTransaction();

        if ($statement) {

            // プレースホルダへ実際の値を設定する
            $statement->bindValue(':name', $_SESSION['name'], PDO::PARAM_STR);
            $statement->bindValue(':email',  $_SESSION['email'], PDO::PARAM_STR);
            $statement->bindValue(':password',  $_SESSION['password'], PDO::PARAM_STR);
            $statement->bindValue(':tel',  $_SESSION['tel'], PDO::PARAM_STR);
            $statement->bindValue(':id',  $_SESSION['id'], PDO::PARAM_STR);


            // クエリ実行
            $statement->execute();

            // トランザクションコミット
            $dbh->commit();


        }

    } catch (PDOException $e) {

        $dbh->rollBack();
        print('Error:' .$e->getMessage());
        $errors["error"] = "データベース接続失敗しました。";

    }

}


}





//
// Twig
//

// Composerで作成されたautoload.phpを読み込む
require_once('../vendor/autoload.php');
// Twig_Loader_Filesystemを使う。account_edit_insert.phpからのtemplatesディレクトリを指定。(相対パス)
$loader = new Twig_Loader_Filesystem('../templates');
// $loaderをTwigの環境設定としてTwig instance を生成
$twig = new Twig_Environment($loader);

// render
// render
echo $twig->render('account_edit_insert.html', array(

  'errors' => $errors,

  'name' => $name,
  'email' => $email,
  'password' => $password,
  'tel' => $tel

  )
);

・接続DBは portfolioデータベースの中の usersテーブル
イメージ説明
イメージ説明

追記 エラー

Error:SQLSTATE[HY000] [1045] Access denied for user 'andrew'@'localhost' (using password: YES)

mysql 権限
イメージ説明
mysql テーブル
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m6u

    2018/02/20 20:27

    該当ソースコードの他のソースで正常にトランザクション処理ができていてこのソースだけできていないのか、それとも全部できないのか。また、接続先データベースの情報も欲しいです。そもそもトランザクション処理に対応しない設定(?)で構築されていたりしないか、とか。

    キャンセル

  • dog57

    2018/02/20 20:58

    他のソースコードでは、エラーはでません。このソースコードだけできていません。どういったDB情報を追加したらよいでしょうか?

    キャンセル

  • dog57

    2018/02/20 21:02

    DB情報を画像で追加しました!

    キャンセル

  • asahina1979

    2018/02/20 22:05 編集

    机上デバックレベルで参加所でおきえるな。

    キャンセル

回答 2

checkベストアンサー

+1

おそらくnew PDOの箇所で例外が発生していると思います。
なぜ、そう言えるかですが、以下に説明されているように、PDOは明示的にPDO::ERRMODE_EXCEPTIONを指定しない限り、例外は発生しないからです。

PHP: エラーおよびエラー処理 - Manual

PDO::ERRMODE_SILENT
デフォルトのモードです。ステートメントおよびデータベースオブジェクトの エラーについて、PDO は単にそのエラーコードのみを設定します。

注意:
PDO::__construct() は、接続に失敗した場合は常に PDOException をスローします。 これは、現在設定されている PDO::ATTR_ERRMODE が何であっても同じです。例外を処理しないと、fatal エラーとなります。

では、なぜrollbackに失敗しているようなエラーメッセージになっているかですが、以下のように、エラーメッセージを表示する前にrollbackメソッドを呼んでいるからです。この時点でもエラーになり、そのエラーが表示されています。

    } catch (PDOException $e) {
        $dbh->rollBack();
        print('Error:' .$e->getMessage());
        $errors["error"] = "データベース接続失敗しました。";
    }

さしあたり、rollback処理を削除して、エラーメッセージを表示させてください。どっちみち、今のスクリプトでは、例外発生時は接続に失敗しているので、rollbackの必要はないはずです。
あるいは、意図したような例外処理にするには、以下を追加することです。

$dbh = new PDO($dsn, $user, $password, array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ));

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/22 19:19 編集

    ありがとうございます。

    次は、質問投稿欄の1番下に追記したエラーが出ました。
    DBに関する設定は大丈夫だと思っていまして、どこか書き方が間違っているかと自分では思っています。

    どんな原因が考えられますでしょうか?

    キャンセル

  • 2018/02/23 09:58

    「DBに関する設定は大丈夫だ」と第三者が見ても思えるような情報提供がないのでなんともいえませんが、
    エラーメッセージ的にはandrewってユーザーのアクセスを拒否しているので、
    mysql内のユーザーandrewが登録されていないとか、パスワード間違いか、権限不足かと思われます。
    os上ユーザーのパスワードと、mysql内ユーザーのパスワードはまた違う扱いだろうと思いますし、
    同じパスワードを使いまわしたりは通常はしないものと思われます。

    DB接続のみのシンプルなphpコードを書いて、
    本当にDB接続ができるのかを試したら良いと思います。

    キャンセル

  • 2018/02/23 11:31

    ここまでいくとユーザーの作り方を失敗してるな

    キャンセル

0

1. DB接続失敗時の例外

PDO::__construct() は、 指定されたデータベースへの接続に失敗した場合、 PDOException を投げます。

2. プリコンパイル時の例外 

もしデータベースサーバーが正常に文を準備する場合、 PDO::prepare() は PDOStatement オブジェクトを返します。 もしデータベースサーバーが文を準備できなかった場合、 PDO::prepare() は FALSE を返すか PDOException を発行します (エラー処理 の方法に依存します)。

3. トランザクション処理開始失敗時の例外

トランザクションが既に開始されている場合や、ドライバがトランザクションに対応していない場合に PDOException をスローします。

あたりは見た目で気づきましょう

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/21 10:15

    public bool PDO::beginTransaction ( void ) 成功した場合に TRUE を、失敗した場合に FALSE を返します。 →失敗してもPDOExceptionをthrowしないから、別途検出ロジック必要。っと勝手に補足させていただきます。

    キャンセル

  • 2018/02/21 10:29

    beginTransaction

    http://php.net/manual/ja/pdo.begintransaction.php

    トランザクションが既に開始されている場合や、ドライバがトランザクションに対応していない場合に PDOException をスローします。

    キャンセル

  • 2018/02/21 12:39

    お手数おかけしてすみません。

    キャンセル

  • 2018/02/23 20:27

    解決致しました。

    ご協力ありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる