PHPに惚れました

今でも多分惚れてます。

さくらレン鯖でMDB2のquote,escapeメソッドでエスケープ処理時に文字化けする#3(解決)

PEARMDB2パッケージを使用した際、escape,quoteメソッドで文字化けが発生。
それの解決メモ。
環境:さくらインターネット - Standardプラン

DBの文字コード周辺の設定値
character set client utf8
(グローバル値) ujis
character set connection utf8
(グローバル値) ujis
character set database ujis
character set filesystem binary
character set results utf8
(グローバル値) ujis
character set server ujis
character set system utf8
collation connection utf8_general_ci
(グローバル値) ujis_japanese_ci
collation database ujis_japanese_ci
collation server ujis_japanese_ci

<?php
//修正前
require_once ("MDB2.php");

$dsn = array(
    'phptype'  => "mysql",
    'username' => "***",
    'password' => "***",
    'hostspec' => "***",
    'database' => "***",
);
$db=MDB2::singleton($dsn);
$db->query("SET NAMES utf8");

print $db->escape("さんし'ゃいん",true);   //'\さ\ん\'\し\ゃ\い\ん' 文字化け・・・
?>

色々調べてみると「SET NAMES utf8」での文字コード指定は非推奨らしい。
mysql_set_charset("utf8"); で指定しなさいと書いてあった。

MDB2PHPのバージョンやmysql項目の設定が整っていて、MDB2_Driver_mysqlのバージョンが1.5以上ならば内部的にmysql_set_charset(); を実行してくれる。
整っていなければSet Namesで文字コードが設定される。

早速MDB2_Driver_mysqlのバージョンが1.4台だったのでバージョンアップ。
これで環境は整った。と思ってさっきのコードを実行してみたが。。。文字化けは直らず。

mysql_client_encodingで確認してみるとujisのまま。

<?php
//修正前
require_once ("MDB2.php");

$dsn = array(
    'phptype'  => "mysql",
    'username' => "***",
    'password' => "***",
    'hostspec' => "***",
    'database' => "***",
);
$db=MDB2::singleton($dsn);
$db->query("SET NAMES utf8");
mysql_set_charset("utf8");

print $db->escape("さんし'ゃいん",true);   //'さんし\'ゃいん'  //直った!!けどなんだかなぁ。
?>

更に色々調べてみるとdsnの設定を配列じゃなくて文字列設定している記事が比較的多い。
真似して以下のように修正。

<?php
//修正後
require_once ("MDB2.php");
$dsn = 'mysql://user:pass@hostspec/dbname?charset=utf8';
$db=MDB2::singleton($dsn);

print $db->escape("さんし'ゃいん",true);   //'さんし\'ゃいん'  //直った!!
?>

結論:

    • MDB2_Driver_mysqlのバージョンは1.5以上
    • dsnの指定は配列ではなく文字列形式

こんなところでしょうか?

さくらレン鯖でMDB2のquote,escapeメソッドでエスケープ処理時に文字化けする#2

あれから色々調べて行き着いたところ。

MDB2のescape()やquote()は内部でmysql_real_escape_string()を使用してる。

このmysql_real_escape_string()はMysqlののcharacter set clientを元にエスケープ処理してくれる。

つまりMysqlとクライアントの文字コードの設定によっては文字化けが起こったり正しくエスケープしてくれなかったりするみたい。

でもcharacter set clientの値utf8になってるんだけどなぁ・・・
それでもまだ化ける。

スクリプト側で対応するしかないのかな。

PHP: mysql_real_escape_string - Manualから引用

<?php
//擬似エスケープ処理関数
function escape($str){
	$search=array("\\","\0","\n","\r","\x1a","'",'"');
	$replace=array("\\\\","\\0","\\n","\\r","\Z","\'",'\"');
	return str_replace($search,$replace,$str);
}
?>

さくらレン鯖でMDB2のquote,escapeメソッドでエスケープ処理時に文字化けする

なぜか分からない。

文字コードはほぼ全てUTF-8に統一してるはず。
DBへの接続時に「SET NAMES utf8」で文字コードも明示的に指定してる。

<?php
require_once "MDB2.php";

$dsn=array("phptype"=>"mysql","username"=>"***","password"=>"***","hostspec"=>"mysql***.db.sakura.ne.jp","database"=>"***");
$db=MDB2::singleton($dsn);
$db->query("SET NAMES utf8");
	}
$txt = $db->quote("さんし'ゃいん","text");  //[さんし\'ゃいん]と変換されればOK
print $txt;
?>

実際の出力
'\さ\ん\'\し\ゃ\ん'

あれれ。
余計なところにバックスラッシュが入ったり、エスケープする文字によってははたまた文字化けしたり。


色々試してみました。

ddhostのサーバーで同じテストコードを実行。こちらはすんなり変換してくれました。
phpinfo()でPHPの設定を両者比較。

文字化けするさくらレン鯖でmagic_quotes_gpcの項目がOnになってる。
コレは習慣としてOffにしておく必要がある設定項目です。
他所さんの記事でもOffにしろ!と良く見かけます。

Get/Post/Cookieで勝手にエスケープしちゃうんですよね。
自分でしないで済むんだからいいじゃない!と思いがちでしょうがコレが逆に怖いんです。

今回はGetやPostなどユーザーからの入力値を処理するわけでなく直接変数に入れた値のエスケープ処理なのでこれは関係ありませんでした。


今度はMDB2のパッケージとMysqlのドライバを最新版に。
したけど変化なし。

ファイルの文字コードをShift-jisで保存して実行もしてみましたが。。。ダメ。

mixiのコミュにも質問を投下しました。そこで頂いた回答の中に、

文字コードの正解はEUCだったりしませんか?」

そう言えばEUCでは試してない。早速。。。
結果、大当たり。

結局文字コードかよ!
文字化けっぽいことになってるんだから文字コードなのは当たり前ですけども!
でもメソッド実行前に$db->query("SET NAMES utf8");してるよなぁ。
でも化ける。どこの文字コードの設定がまだEUCになってるんだろ。

php.iniで設定すべきところは全てutf8にしてある。
となると、やはり疑うべきはMysql
早速確認。

大抵の項目はデフォルトのujisからUTF-8に指定しなおしてます。

character set server・・・・ ujis
collation database・・・・・ ujis_japanese_ci
collation server・・・・・・ ujis_japanese_ci

コレか!?これなのか?

MySQL日本語の旅 | OSS-Web
http://oss.timedia.co.jp/show/MySQL%e6%97%a5%e6%9c%ac%e8%aa%9e%e3%81%ae%e6%97%85

↑のサイトを参考に手直し。
set collation database = "utf8_general_ci" と set collation_server = "utf8_general_ci"を実行してみてみる。

・・・反映されてない。

さて、どうしたものか・・・

前科

鯖管のブログでこっそり紹介されてたので反応。
http://www.ddhost.jp/blog/2010/08/mysql_max_user_connections.html

ニコエコ作りたての頃、色々やっちゃいました。

Mysqlを爆撃。webサーバーを爆撃。
先日もMysqlで20万件+13万件のテーブルの結合しようとしたら結果爆撃。

油断って怖いです。

サイト公開始めた当初はほぼ「俺専用」サイトだったので好きなタイミングで好きな箇所を変更したりもできたもんですが、
googleなどの検索エンジンに拾われるようになったりブログやSNSのマイページからサイトに来てくれる方が増えてきました。

それでもまだ利用者は学校の友人など、身内が多かった頃。
さすがに負荷も考えるようになりました。

でも真っ先に負荷を減らしたのはニコニコのAPIへのリクエスト回数を減らす事。
利用してる鯖のDBには瞬間的に2000〜4000クエリ/回と非力鯖(鯖管曰く)になかなかの高負荷。

落ちないギリギリまでクエリ数を減らしてたんですがついにデータベースへの最大接続数制限食らいました。
SQLの見直しなど出来ることはしたつもり。

今もまだキツイところがあるのか。。。
今の今まで知りませんでした。
結構落ち着いてきて安定期に入ったかと思ってた。^^;

アクセス解析

来訪者ドメインまとめてみる。
いつも通り企業の場合、企業名は伏せてあります。

仕事中こっそりニコ動見に来てるって事ですからね。

教育関係

企業

    • 株式会社ディーエム*ス
    • *芝
    • 株式会社新潟通*サービス
    • 大日本印*
    • KNT北日本通信工*株式会社

ネカフェ

卒業したはずのうちの学校からもまだアクセスが、
後輩が使ってるんでしょうかね。

PHPでちょっと奥に突っ込んだQRコード生成

今更ながら、な記事かもしれませんが。

PEARパッケージ「Image_QRCode」でQRコードを生成出来ます。
よく出回ってるサンプルソース。

Image_QRCode
http://pear.php.net/package/Image_QRCode/download

<?php 
require_once("Image/QRCode.php"); 

$qr = new Image_QRCode(); 
$qr->makeCode("Hello, world"); 
?> 

特に何も意識せず使う分には上記で十分です。
オプションを加えて少し出力に変化を持たせたい場合はこちら

<?php 
require_once("Image/QRCode.php"); 

$qr = new Image_QRCode(); 
$option = array(
	"module_size"=>2,     //サイズ=>1〜19で指定
	"image_type"=>"jpeg",   //画像形式=>jpegかpngを指定
	"output_type"=>"display",  //出力方法=>displayかreturnで指定 returnの場合makeCodeで画像リソースが返される
	"error_correct"=>"H"     //クオリティ(L<M<Q<H)を指定
);
$qr->makeCode("Hello, world",$option); 
?> 

連結QRコードも作れる

連結QRコード。あんまり聞き慣れないと思いますが
簡単に言えばQRコードを分割して読み込んで一つのデータとして読み取る方法です。
f:id:Xephy:20100420230455j:image
↑と↓三つを読み込んだデータは同じです。連結データは読み込む順番もバラバラでも構わないです。
f:id:Xephy:20100420230454j:image
f:id:Xephy:20100420230453j:image
f:id:Xephy:20100420230452j:image

利用方法

オプションとしてもたせられる値は4つ。

    • nの値で何分割するかを指定、
    • mの値で今何分割目のデータを出力かを指定、

nとmは必須となるでしょう。

    • parityで属性を指定(同じ属性値を持たせないと関連付けられたものとみなさい→読み取りエラー)

ここは省略可能です。

    • originaldataで元データ。こっちも同じ値をもたせないよ読み取りエラーに。 詳細は不明。

これも省略可能。

"n"=>3, //データを分割する数(この場合3分割)
"m"=>1, //何分割目か(3分割中1つ目のデータとして出力)
"parity"=>"", //属性(無指定)
"originaldata"=>"" //元データ(無指定)

3分割中1つ目のデータを吐き出す

<?php 
require_once("Image/QRCode.php"); 

$qr = new Image_QRCode(); 
$option = array(
	"module_size"=>2, 
	"image_type"=>"jpeg",
	"output_type"=>"display",
	"error_correct"=>"H"
);
$append=array(
	"n"=>3,  //データを分割する数
	"m"=>1,  //何分割目か
);

$qr->makeCode("Hello, world",$opption,$append); 
?> 

3分割中2つ目のデータを吐き出す

<?php 
require_once("Image/QRCode.php"); 

$qr = new Image_QRCode(); 
$option = array(
	"module_size"=>2, 
	"image_type"=>"jpeg",
	"output_type"=>"display",
	"error_correct"=>"H"
);
$append=array(
	"n"=>3,  //データを分割する数
	"m"=>2,  //何分割目か
);

$qr->makeCode("Hello, world",$opption,$append); 
?> 

3分割中3つ目のデータを吐き出す

<?php 
require_once("Image/QRCode.php"); 

$qr = new Image_QRCode(); 
$option = array(
	"module_size"=>2, 
	"image_type"=>"jpeg",
	"output_type"=>"display",
	"error_correct"=>"H"
);
$append=array(
	"n"=>3,  //データを分割する数
	"m"=>1,  //何分割目か
);

$qr->makeCode("Hello, world",$opption,$append); 
?> 

簡単ですね!

携帯用コンテンツ制作に役立ちそうなサイトメモ

携帯用コンテンツを作るのって初めはすごい大変です。

まさに今大変な目にあってます。
もうキャリア毎に制約がとても多いです・・・

と、言うわけで自分が色々調べてためになりそうだなと思ったサイトまとめ