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

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

新規登録して質問してみよう
ただいま回答率
85.48%
WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

2回答

1688閲覧

PHPで全てのエラーを catch したい(WordPressにお詳しい方へ)

gorimaz

総合スコア26

WordPress

WordPressは、PHPで開発されているオープンソースのブログソフトウェアです。データベース管理システムにはMySQLを用いています。フリーのブログソフトウェアの中では最も人気が高く、PHPとHTMLを使って簡単にテンプレートをカスタマイズすることができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

2グッド

1クリップ

投稿2022/03/13 14:56

編集2022/03/14 14:08

前提

WordPress特有の仕様についての質問になります。
お詳しい方宜しくお願い致します。

実現したいこと

2つのテーブルを 「厳密に」 LOCK TABLESし、ROLLBACKしたいです。

「厳密に」 とは 「一方のテーブルだけが更新されることを絶対になくしたい(2つのテーブルを絶対に一緒に更新したい)」 という意味です。

一般的に$wpdb->last_errorの場合に自ら throw~catch し、ROLLBACKさせると思います。
今回はPHPのNoticeエラーの場合でも throw~catch し、ROLLBACKさせたいのです。

発生している問題

該当のソースコードのように、Noticeエラーの場合でもそれを throw~catch させようしてset_error_handlerを書きました。

しかしこうすると次の errorMessage のように、なぜかwp_optionswp_commentsLOCK TABLESを要求されます。

(どこにもwp_optionswp_commentsを更新するような処理は書いていないのに、です。)

errorMessage

1[13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_options' was not locked with LOCK TABLES for query SELECT option_value FROM wp_options WHERE option_name = '_site_transient_update_plugins' LIMIT 1 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_updates_menu, wp_get_update_data, get_site_transient, get_site_option, get_network_option, get_option, QM_DB->query 2 3[13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_options' was not locked with LOCK TABLES for query SELECT option_value FROM wp_options WHERE option_name = '_site_transient_update_themes' LIMIT 1 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_updates_menu, wp_get_update_data, get_site_transient, get_site_option, get_network_option, get_option, QM_DB->query 4 5[13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_options' was not locked with LOCK TABLES for query SELECT option_value FROM wp_options WHERE option_name = '_site_transient_update_core' LIMIT 1 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_updates_menu, wp_get_update_data, wp_get_translation_updates, get_site_transient, get_site_option, get_network_option, get_option, QM_DB->query 6 7[13-Mar-2022 14:14:33 UTC] WordPress データベースエラー: Table 'wp_comments' was not locked with LOCK TABLES for query 8 SELECT comment_approved, COUNT( * ) AS total 9 FROM wp_comments 10 11 GROUP BY comment_approved 12 made by require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/apple/index.php'), get_footer, locate_template, load_template, require_once('/themes/apple/footer.php'), wp_footer, do_action('wp_footer'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_render, do_action_ref_array('admin_bar_menu'), WP_Hook->do_action, WP_Hook->apply_filters, wp_admin_bar_comments_menu, wp_count_comments, get_comment_count, QM_DB->query 13

該当のソースコード

ロック対象の2つのテーブルです。text atag aという値が入っています。

問題のソースコードは後述のupdate()で生じます。

SQL

1CREATE TABLE my_texts ( 2 ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT 3 ,text VARCHAR(10) NOT NULL 4 ,PRIMARY KEY (ID) 5); 6INSERT INTO my_texts (text) VALUES ('text a'); 7 8CREATE TABLE my_tags ( 9 ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT 10 ,tag VARCHAR(10) NOT NULL 11 ,PRIMARY KEY (ID) 12); 13INSERT INTO my_tags (tag) VALUES ('tag a');

以下がupdate()です。

上記のtext atag aを、text btag bへ更新しようとしますが、11行目のtag_dataのキーがidでなくxxであるために、59行目で「Notice: Undefined index」を生じさせています。

このエラーを throw~catch させるべく、set_error_handlerを書いたのですが、すると上記の errorMessage が表示されてしまうのが問題です。

PHP

1// PHPのNoticeエラーの場合でも throw~catch 2function exceptions_error_handler($severity, $message, $filename, $lineno) { 3 throw new ErrorException($message, 0, $severity, $filename, $lineno); 4} 5 6// 更新データ 7$post_data = [ 8 'id' => 1, 9 'text' => 'text b', 10 'tag_data' => [ 11 'xx' => 1, // 「Notice: Undefined index」 12 'tag' => 'tag b' 13 ] 14]; 15 16// 更新を実行 17$posted = update( $post_data ); 18var_dump( $posted ); 19 20// 更新の関数 21function update( $post_data ){ 22 global $wpdb; 23 24 // PHPのNoticeエラーの場合でも throw~catch 25 set_error_handler('exceptions_error_handler'); 26 27 $result = ['status'=>'error']; 28 try { 29 $is_error = null; 30 31 // テーブルのロック 32 $wpdb->query('SET autocommit=0'); 33 $sql = 'LOCK TABLES my_texts WRITE, my_tags WRITE'; 34 if ( $wpdb->query($sql) === false ) { 35 $is_error = true; 36 $mes = 'テーブルのロックで失敗'; 37 error_log( $mes . ' $sql = ' . json_encode($sql) ); 38 throw new Exception( $mes ); 39 40 } else { 41 42 // my_texts の投稿 43 $sql = 'UPDATE my_texts SET text = %s WHERE ID IN ( %d )'; 44 $prepares = [ $post_data['text'], $post_data['id'] ]; 45 $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$prepares ) ); 46 if ( $wpdb->last_error ) { 47 $is_error = true; 48 $mes = 'my_texts の get_results で失敗'; 49 error_log( $mes . ' $sql = ' . json_encode($sql) ); 50 throw new Exception( $mes ); 51 52 } else { 53 54 // タグ情報 55 $tag_data = $post_data['tag_data']; 56 57 // my_tags の投稿 58 $sql = 'UPDATE my_tags SET tag = %s WHERE ID IN ( %d )'; 59 $prepares = [ $tag_data['tag'], $tag_data['id'] ]; // 「Notice: Undefined index」 60 $rows = $wpdb->get_results( $wpdb->prepare( $sql, ...$prepares ) ); 61 if( $wpdb->last_error ){ 62 $is_error = true; 63 $mes = 'my_tags の get_results で失敗'; 64 error_log( $mes . ' $sql = ' . json_encode($sql) ); 65 throw new Exception( $mes ); 66 67 }else{ 68 $result = ['status'=>'ok']; 69 } 70 71 } 72 } 73 74 // ロック解除 75 if( $is_error ){ 76 $wpdb->query('ROLLBACK'); 77 $wpdb->query('UNLOCK TABLES'); 78 error_log( 'ROLLBACK しました' ); 79 80 }else{ 81 $wpdb->query('COMMIT'); 82 $wpdb->query('UNLOCK TABLES'); 83 error_log( 'COMMIT しました' ); 84 } 85 86 } catch (Exception $e) { 87 $result = $e->getMessage(); 88 89 } finally { 90 restore_error_handler(); 91 } 92 93 return $result; 94}

試したこと

errorMessage のいう事を素直に聞いてwp_optionswp_commentsLOCK TABLESを次のように実行すると、errorMessage はなくなりました。

PHP

1$sql = 'LOCK TABLES my_texts WRITE, my_tags WRITE, wp_options WRITE, wp_comments WRITE';

しかし、そもそもwp_optionswp_commentsLOCK TABLESがなぜ必要なのかわからないので、これであっているのか?とても不安な心境です。

そこで、2つのテーブルを 「厳密に」 LOCK TABLESするような方法として、適切な処理を求めて質問させて頂きました。

PHPのNoticeエラーの場合でもROLLBACKさせるにあたっては、どのような処理が良いのでしょうか?

長い質問で申し訳ございませんが、どなたかWordPressにお詳しい方がいらっしゃいましたら何卒、宜しくお願い致します。

理由不明だが解決した方法(2022/03/14 23:09)

上記「// ロック解除」の処理を下記のようにしたところ、 「厳密に」 LOCK TABLESもでき、errorMessage もなくなりました。しかしなぜ解決したのか?がわからずやはり不安な心境です。正しい処理なのか、他に問題はないのか、大いに疑問です。

原因がお分かりになる方いらっしゃいましたらご教示頂けませんでしょうか。

■ 上記「// ロック解除」の処理↓

php

1 // ロック解除 2 if( $is_error ){ 3 $wpdb->query('ROLLBACK'); 4 $wpdb->query('UNLOCK TABLES'); 5 error_log( 'ROLLBACK しました' ); 6 7 }else{ 8 $wpdb->query('COMMIT'); 9 $wpdb->query('UNLOCK TABLES'); 10 error_log( 'COMMIT しました' ); 11 } 12 13 } catch (Exception $e) { 14 $result = $e->getMessage(); 15 16 } finally { 17 restore_error_handler(); 18 } 19 20 return $result; 21}

■ 理由不明だが解決した方法↓

php

1 // ロック解除 2 $wpdb->query('COMMIT'); 3 error_log( 'COMMIT しました' ); 4 5 } catch (Exception $e) { 6 $result = $e->getMessage(); 7 $wpdb->query('ROLLBACK'); 8 error_log( 'ROLLBACK しました' ); 9 10 } finally { 11 restore_error_handler(); 12 $wpdb->query('UNLOCK TABLES'); 13 } 14 15 return $result; 16}
nikuatsu, arcxor👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

エラーメッセージに出てるtransientはメインクエリの
投稿検索結果のキャッシュの処理に係るものと思われます

推測ですがSTART TRANSACTION句がないために
メインクエリのSQLまでロールバックしようとしてしまっているのではないでしょうか

ちゃんと追いかけてはいないですが、WordPressは元々1プロセス中のSQLを
まとめてtransactionとしてrollbackできるようにしてるんじゃないかと思いますので
SAVEPOINTを指定して入れ子のtransactionを開始し
そこにROLLBACK TOするようにしてみると解消するかもしれません

投稿2022/03/14 05:36

編集2022/03/14 05:37
KazuhiroHatano

総合スコア7804

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gorimaz

2022/03/14 14:10

また詳しくありがとうございます。 > 推測ですがSTART TRANSACTION句がないために 試しに 'SET autocommit=0' の替わりに 'START TRANSACTION' にしてみましたが、全く同じ errorMessage でした。 > SAVEPOINTを指定して入れ子のtransactionを開始し > そこにROLLBACK TOする こちらは使い方を勉強中で、まだ試せていません。 ただその前に、質問に追記した「理由不明だが解決した方法(2022/03/14 23:09)」で解決しまして、しかしなぜ解決したのか不明です。 もしこの解決した方法についてお心当たりございましたらまたお知恵を拝借できませんでしょうか。
KazuhiroHatano

2022/03/14 16:04 編集

LOCK TABLESでTRANSACTION開始した場合は一度閉じて開始するんですね、 では入れ子になってるTRANSACTIONのROLLBACKが外側まで戻ってるというのはなさそうですね コードを見るに、修正前は例外を投げられるとTRANSACTIONが開いたままになってしまうかと思います 自前のSQLでLOCK TABLESすることで閉じたWordPressによるTRANSACTIONが LOCKしていたテーブルと自前のSQLのLOCKしたテーブルが一致していないために 本来WordPressによるTRANSACTIONがCOMMITするはずのところでエラーになってる、という感じでしょうか
gorimaz

2022/03/14 17:11

> 修正前は例外を投げられるとTRANSACTIONが開いたままになってしまう あぁ!なるほど! set_error_handler を書いたことによって、NOTICEエラーのときに処理が catch に飛んでしまい、だから if( $is_error ){ }else{ } の if の中も else の中も通らず、それらの中の 'UNLOCK TABLES' は実行されず、TRANSACTIONが開いたままになってしまう。ですか。 さすがです。拙いコードの解読をして頂きありがとうございます。 そしてこの場合に、 > 本来WordPressによるTRANSACTIONがCOMMITするはずのところ と混ざってしまう。わけですね。 いや、ご慧眼に感服致します。 どうもありがとうございました。
guest

0

エラーハンドラをセットし

PHP

1set_error_handler('myErrorHandler'); 2function myErrorHandler($errno, $errstr, $errfile, $errline){ 3 throw new exception(); 4 return true; 5}

tryでcatchしてください

PHP

1try{ 2}catch(Exception $e){ 3 //ロールバック? 4 die($e->getMessage()); 5}catch(PDOException $e){ 6 die($e->getMessage()); 7}

投稿2022/03/14 00:48

yambejp

総合スコア114829

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gorimaz

2022/03/14 13:36

そのエラーハンドラを書いて問題が生じると質問に書いたつもりなのですが、どんな違いを意図していらっしゃいますか?それともお目に留まらなかっただけでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問