[wp×SQLite]"PDO (SQLite) For WordPress"の問題点まとめ

WordPressでSQLiteを使うプラグイン『PDO (SQLite) For WordPress』

SQLite Integration』に乗り換えてX年。
つまり『PDO (SQLite) For WordPress』使わなくなって、はやX年。

今まで見つけた問題点と修正内容。
アクセスもないけど、せっかく調べたのに消すのは忍びなく、かといえページを食われるのも面白くない。

なので、今まで発見した問題点をまとめておくことにした。
誰のためでもない。自分のためである。

Warning: array_merge()問題

コメントを編集する際にエラーが出てきた。

Warning: array_merge() [function.array-merge]: Argument #1 is not an array in /XXX/YYY/ZZZ/public_html/wp-includes/comment.php on line 1097

array_mergeに入れるのは両方配列でないといけない。
つまりどっちかが『配列じゃねぇ!!』てことみたいだ。

みてみたら$commentが『Array()』ではなくて『Array』。
確かに配列ではない。でもArray。
どゆこと!?
て思いながらとりあえずの修正。

/wp-includes/comment.phpを修正

以下の13行目を追記

function wp_update_comment($commentarr) {
	global $wpdb;

	// First, get all of the original fields
	$comment = get_comment($commentarr['comment_ID'], ARRAY_A);

	// Escape data pulled from DB.
	$comment = $wpdb->escape($comment);

	$old_status = $comment['comment_approved'];

	// Merge old and new fields with new fields overwriting old ones.
	if(count($comment) > 1)	// @to
	$commentarr = array_merge($comment, $commentarr);

	$commentarr = wp_filter_comment( $commentarr );

コメント出来た。

日付から記事取得

WordPressでSQLiteを使うと、年・月・日 つまりは日付から記事を読み出すのがうまくいかない。
これはMySQLとSQLiteとでSQL文が違うために起きている。

言わば、『MySQLとは違うのだよ MySQLとは!!!』ということ

『YEAR(wp_posts.post_date)=2009』は
 『wp_posts.post_date like "2009-%"』か
 『strftime('%Y',wp_posts.post_date)='2009'』

『MONTH(wp_posts.post_date)=04』は
 『wp_posts.post_date like "%-04-%"』か
 『strftime('%m',wp_posts.post_date)='04'』

『DAYOFMONTH(wp_posts.post_date)=04』は
 『wp_posts.post_date like "%-04-%"』か
 『strftime('%d',wp_posts.post_date)='04'』
に修正が必要

/wp-content/pdo/db.php

直後に

if( strtolower(mb_substr(str_replace(" ","",$query),0,6)) == "select" )
{
	if( stristr($query, 'where') )
	{
		$arr_query[0] = preg_replace("/(SELECT .* FROM .* WHERE) (.*)/i", "$1" , $query);
		$arr_query[1] = preg_replace("/(SELECT .* FROM .* WHERE) (.*)/i", "$2" , $query);
		$explode = 1;
		if($arr_query[0] == $arr_query[1])
		{
			$explode = 0;
		}			
		$arr_query[1]	= preg_replace("/YEAR\(wp_posts\.post_date\)=([0-9]*[0-9])/i", "strftime('%Y',wp_posts.post_date)='$1'" , $arr_query[1]);
		$arr_query[1]	= preg_replace("/DAYOFMONTH\(wp_posts\.post_date\)=([0-9]*[0-9])/i", "strftime('%d',wp_posts.post_date)='$1'" , $arr_query[1]);
		$arr_query[1]	= preg_replace("/MONTH\(wp_posts\.post_date\)=([0-9]*[0-9])/i", "strftime('%m',wp_posts.post_date)='$1'" , $arr_query[1]);
		if($explode == 1)
		{
			$query	= $arr_query[0]." ".$arr_query[1];
		}else
		{
			$query	= $arr_query[1];
		}
	}
}

※現状『YEAR(wp_posts.post_date)=2009』とか書いたとき修正されない様子…
ページ送り『SELECT FOUND_ROWS()』対応とセットで修正が必要

まだ完全ではないし他にもカラム追加系とか
SQLiteはデータ型もたなくて、自動noが1個しか使えないみたい?とかで
色々修正が必要になる様子。

アクセス時にtimeoutエラー

本日のブラウザ初回起動時とかに下記が出ることがある。

Fatal error: Maximum execution time of 30 seconds exceeded in XXX\XXX\wp\wp-content\pdo\PDOEngine.php on line XXX

どうにもPHPでタイムアウトらしい。

でも鯖缶様に結構長めにして貰ってあるので、きっとPCを起動する際に読み損ねてるんだろうと思っていた。

ところがどっこいPDOEngine.php内で設定しちゃっていたのだ。

/wp-content/pdo/PDOEngine.php

private function connect($connectionParams){
	set_time_limit(30);
	global $wpdb;

private function connect($connectionParams){
	set_time_limit(300);
	global $wpdb;

に修正。

300秒待たせれば、きっと大丈夫…と思う。
各々の環境によるから…。

存在しないカテゴリ名使うとPDOエラー

PATH_INFO形式のとき
http://example.com/category/aaaa/
みたいに存在しないカテゴリ名前を出すとPDOエラーが出る
そんなときの対処

wp-content/pdo/PDOEngine.php

private function executeQuery( $statement )内

if (count($this->extractedVariables) > 0){
	$this->queries[] = "Executing:\t ".print_r($this->extractedVariables, true);

if (count($this->extractedVariables) > 0){
	//$this->queries[] = "Executing:\t ".print_r($this->extractedVariables, true);
	$this->queries[] = "Executing:\t (nodata)";

とする。

「全部出そうが出すまいが、データがないもんはないので知らんわい!」という対処。

ページ送り『SELECT FOUND_ROWS()』対応

categoryとかarchiveとか一覧表示すると次のページとかのリンクが出ない。
そんな条件がなければ出るのに…

つまりページ数認識ではないか?

調べてみたところ、結構な記事数があるカテゴリでも
『$wp_query->max_num_pages』が『1ページ』となってしまっている。

つまりSQL文がオカシイ

こんな単純なミスが修正されないハズがないので、
またSQLiteとMySQLとの仕様差異と皆気づくところ。

WP_Queryはwp-includes/query.phpにあるので確認したところ、そりゃ当然アタリ。

原因は『SELECT FOUND_ROWS()』

『SELECT FOUND_ROWS()』は
MySQLなら直前に実行したSQLの件数を返してくれる。
SQLiteにはない。

対処するために直前のSQLを保持しておいてカウントするように書き換えた。

対処法

/wp-content/pdo/db.phpを書き換える

function query($query)

直後に下記を追加。

if( strtolower(mb_substr(str_replace(" ","",$query),0,6)) == "select" )
{
	if($query == "SELECT FOUND_ROWS()"){
		$nowquery = $query;
		$query = $this->beforequery;
		$query  = preg_replace("/(SELECT .* FROM)/i", "SELECT COUNT(*)  FROM" , $query);
		$query  = preg_replace("/((GROUP BY|ORDER BY|LIMIT ).*)/i", "" , $query);
	}else{
		$this->beforequery = str_replace("SQL_CALC_FOUND_ROWS"," ",$query);
	}
}

selectのときだけ処理するようにしている。
日付から記事取得するための修正とセットで修正が必要。

はい、結構な力技。

因みに
SQLでcount(*)とかに直してない場合、勝手に2ページと判断して何故かページが出るようになるが、最大も最小もなく2ページだから正しくはない。

ページの編集が巧く使えない

あくまで『ページ編集』だけ。
原因は$hierarchical_post_types[0]がうまく取得出来ないとこにあった。
SQLiteとMySQLで使えるSQLが違うから、他にも出てくるかもしれない。

/wp-includes/post.php修正

function wp_unique_post_slug(~)内

 $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( '" . implode("', '", $wpdb->escape($hierarchical_post_types)) . "' ) AND ID != %d AND post_parent = %d LIMIT 1";

if (defined("DB_TYPE") && DB_TYPE == "sqlite") {
	$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( '" . $wpdb->escape($hierarchical_post_types[0]) . "' ) AND ID != %d AND post_parent = %d LIMIT 1";
}else{
	$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( '" . implode("', '", $wpdb->escape($hierarchical_post_types)) . "' ) AND ID != %d AND post_parent = %d LIMIT 1";
}

投稿時の記事数取得

WordPressを3系にしてから投稿は出来るんだけど、フロントからカテゴリが消えてしまうという現象が起きるようになった。

『esc_sql()』というものが配列には対応しておらず、
post_type IN()でこのポストタイプの判断条件がエラーしていることが原因だった。

wp-includes/taxonomy.php

function _update_post_term_count( $terms, $taxonomy ) 内

	$object_types = esc_sql($object_types);
	foreach ( (array) $terms as $term ) {
		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types) . "') AND term_taxonomy_id = %d", $term ) );

	// @to
//	$object_types = esc_sql($object_types);
	// @to
	foreach ( (array) $terms as $term ) {
		// @to
		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . @implode("', '", $object_types) . "') AND term_taxonomy_id = %d", $term ) );
		// @to

とする。

これまでの修正の結果、countは出来ていたので、原因がわからなかった。
SQLをひたすら取得していたよ…。

一部使えないプラグイン対応

例えば
SELECT p.post_title FROM テーブル AS p
というSQLがあるとする。

MySQLなら配列は『$うんつら["post_title"]』として返してもらえるんだけど、
SQLiteだと『$うんつら["q.post_title"]』として返される。

そのせいでSimple Tags等、巧く動かないプラグインが多数ある。

前回同様SQL文を書き換えようと思ったのだけど、
『q.post_title as post_title』みたいに
『AS うんつら』と既に宣言されているものもあるのだから、なかなか巧くいかない。
※q.post_title as post_title AS post_title as post_title てなる

そこで『配列の添字を書き換えて作り直す』修正をする。

/wp-content/pdo/PDOEngine.phpの書き足し+書き換え

class PDO_Engine{~}内の最後の方に、下記を追加。

/// 再帰的配列名変更 @to
function RecurrenceCutExtraAlignmentName($Array){
	if(is_array($Array)){
		foreach($Array as $Akey => $Aval){
			$ArrTname = explode(".",$Akey,2);
			unset($Array[$Akey]);
	
			if(isset($ArrTname[1]))
				$Akey = $ArrTname[1];
	
			$Array[$Akey] = $Aval;
			if(is_array($Aval))
				$Array[$Akey] = $this->RecurrenceCutExtraAlignmentName($Aval);
		}
	}
	return $Array;
}

次に同じファイル内から
private function processResults()を探して、7・8行目を追加。

	private function processResults(){
		if(in_array($this->queryType, array("select", "describe","show")) && $this->needsPostProcessing){
			$this->results = $this->rewriteEngine->processResults($this->_results);
		}else{
			$this->results = $this->_results;
		}
		// @to
		$this->results = $this->RecurrenceCutExtraAlignmentName($this->results);
	}

結構無理矢理。

とりあえずは動いてるから、ログを見ながら様子見。

アップデートの仕方

SQLiteプラグインだけではまともに動いてくれないため、
内部的にも色々書き換えてしまったwordpress。

それをバージョンUPするのは容易じゃない。
自動では巧く行かないし、手動でもちょっとメンドクサイ。
この状態での一番カンタンなバージョンUPの仕方。

バージョンUPする際には
ちょっと使えなくなる時間が出来るから.htaccessとかで
メンテナンスページにでも飛ばした方がいいかも。

説明のための名前規定

wpA … 今使用してるwordpress
wpB … downloadした新しいwordpressを解凍したもの

手順

wpのフォルダ名変更

wpAの名前をテキトウに変更し、wpBの名前をwpAに与える。

たとえばwpAが「/wp]以下にあったとすると
wpA→「/_wp」、wpB→「/wp」
とする。

wpCにwpAの一部をコピー

同じものがある場合、上書き
/wp-content/database (DB)
/wp-content/plugins/ (プラグイン)
/wp-content/themes/ (テンプレート)
/wp-content/pdo (SQLiteプラグイン)
wp-config.php
同じものがある場合、基本的には上書きしない

wp-includes/wp-db.php修正

画面が白くなって使えない場合修正。
このファイル自体はvar upで新しくなるから、必ず『修正』。
function __construct(~)内

$this->dbh = @mysql_connect($dbhost, $dbuser, $dbpassword, true);

if (defined("DB_TYPE") && DB_TYPE == 'sqlite'){}
else{
	$this->dbh = @mysql_connect($dbhost, $dbuser, $dbpassword, true);
}

/wp-includes/post.php修正

ページの編集機能おかしければ修正。
このファイル自体はvar upで新しくなるから、必ず『修正』。
>>修正内容

※作りが変わったので必要なくなった様子

/wp-includes/comment.phpを修正

コメントの編集機能おかしければ修正。
このファイル自体はvar upで新しくなるから、必ず『修正』。
>>修正内容

確認して巧く動いていれば成功。

コメント

  1. [...] MOT:主にプログラム勉強メモ部屋さんの記事を参考にして [...]

タイトルとURLをコピーしました