mb_strlenとかで挙動が変だったのでメモ(php.iniの設定(マルチバイトまわり)とか)[Permanent Link]

プログラムをちょこちょこ書いていると、よく動きがおかしくなって解決に時間を費やしてしまいます。

mbstring.internal_encoding = EUC-JPになっていたせいでUTF-8なアプリの日本語入力が効かなかったりとか。

mb_strlenも、あれ?と思う動きをしてしまいます。プログラムの動きがおかしいときは、大抵自分の書いたコードが間違えているのですけれども(多分)。

んで、mb_strlenです。文字数の判定で使ったところ、実際の文字数と数値が違ってしまいました。試しに以下のようなスクリプトを書いて実験してみます。

<?php
$post_mb_strlen = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit']))
{
    if (isset($_POST['mb_strlen']))
    {
        $post_mb_strlen = $_POST['mb_strlen'];
        $echo_post_mb_strlen_1 = mb_strlen($post_mb_strlen);
        $echo_post_mb_strlen_2 = mb_strlen($post_mb_strlen, 'utf8');
    }
}
?><!DOCTYPE html>

<head>

    <meta charset=utf-8">

    <title>mb_strlenテスト</title>

</head>

<body>

<h1>mb_strlenテスト</h1>

<ul>
<?php
if (! empty($echo_post_mb_strlen_1))
{
    echo '<li>第2パラメータ設定なし : ' . $echo_post_mb_strlen_1 . '</li>';
}
if (! empty($echo_post_mb_strlen_2))
{
    echo '<li>第2パラメータ設定あり : ' . $echo_post_mb_strlen_2 . '</li>';
}
?>
</ul>

<form action="./" method="post">

<p><input type="text" name="mb_strlen" value="<?=$post_mb_strlen?>"></p>

<p><input type="submit" name="submit"></p>

</form>

</body>

</html>

※公開サーバで検証しないようにお願いします。

「abcde」をPOSTすると、以下の結果が出力されました。

第2パラメータ設定なし : 5
第2パラメータ設定あり : 5

次に「あいうえお」をPOSTすると、以下の結果が出力されました。

第2パラメータ設定なし : 15
第2パラメータ設定あり : 5

マニュアルのmb_strlenの項には以下のように書いてあります。

int mb_strlen ( string $str [, string $encoding] )

文字列の長さを取得します。

PHP: mb_strlen – Manual

str

長さを調べたい文字列。

encoding

encodingパラメータには文字エンコーディングを指定します。省略した場合は、内部文字エンコーディングを使用します。

PHP: mb_strlen – Manual

ああ、なるほどー、encodingパラメータを指定していないからだな、と考えてしまうと、その場その場で文字コードに悩まされることになります。Webで調べてみると、encodingパラメータは必ず指定せよ、と教えるページもあるのですが、それ以前に、.htaccessとかスクリプトでPHPの設定をあらかじめ変更しておくのがトラブルが少ないのではないかと思います。

前述の実験では、php.iniの記述が以下のようになっていました。

output_buffering = Off
;default_charset =
;mbstring.language =
;mbstring.internal_encoding =
mbstring.http_input = pass
mbstring.http_output = pass
mbstring.encoding_translation = Off
mbstring.detect_order = Off
;mbstring.substitute_character =

PHPの設定としては、mbstring.language = EUC-JPのように設定してあるよりは、ほぼ何も設定していないこのようにしてあるのが、実際のところ、文字化けなどのトラブルの原因を突き止めるのに有用かなと思います。

これを.htaccessやスクリプトで例えば以下のように変更してみます。

.htaccess
php_value mbstring.internal_encoding UTF-8
スクリプト
ini_set('mbstring.internal_encoding', 'UTF-8');

そうすると、出力は以下のようになりました。

「abcde」をPOSTすると、以下の結果が出力されました。

第2パラメータ設定なし : 5
第2パラメータ設定あり : 5

次に「あいうえお」をPOSTすると、以下の結果が出力されました。

第2パラメータ設定なし : 5
第2パラメータ設定あり : 5

encodingパラメータを指定しなくても、文字数が正しくカウントされています。

んで、php.iniの他の項目は変える必要があるのかないのか疑問に思ってくるので、各項目について調べてみることにします。

output_buffering

デフォルトは"0"

このディレクティブを ‘On’ と設定することにより、全てのファイルに 関して出力バッファリングを有効にすることができます。 特定の大きさにバッファの大きさを制限したい場合、このディレクティブの 値として ‘On’ の代わりに最大バイト数(例:output_buffering=4096) を使用することができます。 PHP 4.3.5 以降、PHP-CLI ではこのディレクティブが常に Off となります。

PHP: 実行時設定 – Manual

これはデフォルトのままでいいんじゃないの?

default_charset

デフォルトは""

4.0.0 以降、PHP は、デフォルトで常にContent-type:ヘッダで character encodingを出力するようになっています。charsetの送信 を無効にするには、これを空にしてください。

PHP: コア php.ini ディレクティブに関する説明 – Manual

ということなので、これもデフォルトのままで。つか、文字コード決め打ちするとメールとか大丈夫なの? と思ってしまうので、header()とか使えばいいんじゃないの?

mbstring.language

デフォルトは"neutral"

mbstring で使用される言語設定(NLS)のデフォルト値。 この設定は mbstring.internal_encoding を定義するため、 php.ini の中で mbstring.internal_encoding は、 mbstring.language の後に置く必要があることに注意してください。

PHP: 実行時設定 – Manual

これが"neutral"でも上のスクリプトでは何も問題ないのですが……。というのも、PHP入門書とかでは'Japanese'にせよ、と書いてあるのです。

試しにmbstring.internal_encodingをコメントアウトして、これを'Japanese'にしてみると(ini_set('mbstring.language', 'Japanese');)、mbstring.internal_encodingはISO-8859-1になって、第2パラメータ設定なしのmb_strlenは正しくカウントされません。

ということで、mbstring.languageのセットは必要ないっぽい。

mbstring.internal_encoding

デフォルトはNULL

内部文字エンコーディングのデフォルト値を定義します。

PHP: 実行時設定 – Manual

これのみ、今回設定を変更しました。設定を変更しないとISO-8859-1になってしまいましたので。マルチバイト関数を使わないなら無視する感じ。

mbstring.http_input

デフォルトは"pass"

HTTP 入力文字エンコーディングのデフォルト値を定義します。

PHP: 実行時設定 – Manual

これこのままで問題あるの? って思うと何も問題ないのでこのまま"pass"で。

mbstring.http_output

デフォルトは"pass"

HTTP 出力文字エンコーディングのデフォルト値を定義します。

PHP: 実行時設定 – Manual

これもmbstring.http_inputと同様、このまま"pass"で。

mbstring.encoding_translation

デフォルトは"0"

入力される HTTP クエリに関して、 文字エンコーディング検出および内部文字エンコーディングへの変換を行う 透過的な文字エンコーディングフィルタを有効にします。

PHP: 実行時設定 – Manual

変換なんてされるとややこしいので、このまま"0"で。

mbstring.detect_order

デフォルトはNULL

文字コード検出のデフォルト値を定義します。

PHP: 実行時設定 – Manual

echo implode(', ', mb_detect_order())で確認するとASCII, UTF-8となる。なんか怪しいので、UTF-8, ASCIIにしておく。

mbstring.substitute_character

デフォルトはNULL

無効な文字を代替する文字を定義します。

PHP: 実行時設定 – Manual

あんまり重要じゃないっぽいのでデフォルトのままでいいのかな。ちなみにデフォルトはNULLだけれどもmb_substitute_character()とかmb_get_info()で調べると63が入ってる。

という感じで、スクリプトでは以下のように設定してみます。

ini_set('output_buffaring', '0');
ini_set('default_charset', '');
ini_set('mbstring.language', 'neutral');
ini_set('mbstring.internal_encoding', 'UTF-8');
ini_set('mbstring.http_input', 'pass');
ini_set('mbstring.http_output', 'pass');
mb_detect_order('UTF-8, ASCII');
ini_set('mbstring.substitute_character', '63');

mbstring.encoding_translationはPHP_INI_PERDIRで、スクリプトでは設定できないので、.htaccessとかで設定。

php_flag mbstring.encoding_translation 0

ini_set('mbstring.http_input', 'pass');が効かないけれども、出力された後だからかな?

あ、あと、mbstring.func_overloadもあったなぁ。

ポイントを現金でキャッシュバック

ファイルアップロード・サムネイル生成でハマる[Permanent Link]

サムネイル生成を作っているときにハマったのでメモ。

2MB以上の画像アップロードできない現象が発生!

ああ、なるほど、upload_max_filesizeがアレなんだな(独り言)。

upload_max_filesize
post_max_size
momory_limit

このあたりの数値を上げて、再度試すがアップロードできない。

なんだかimagecreatefromjpegあたりでひっかっかているっぽい。うーん、これはGDの限界なのか。なんだか1600*1200ピクセル程度の画像でエラーが出ているってサイトもあるなあ。

でも、アップロードに成功したファイルは2592*1944ピクセルで、失敗したファイルは2896*1944ピクセル。デジカメの撮りっぱなしファイル程度をアップロードできないのは仕様としてどうなのよ。と思いつつ、なんでだろ、なんでだろ、としばらく悩む。

画像をPhotoshopで開いてみても、特に問題ない。300dbiってのが気になるけれども単にサイズが大きめなだけ。

それじゃあ、ということで、GDを通さないで、単純にアップロードするだけにしてみる。

これも失敗。状況は変わらず、アップロードできない画像がある。php.iniを直接変更してApacheを再起動している状況でもあるわけで、問題はphp.iniの設定でもなく、GDの問題でもないなあ、これは。

んで、よくよく見ると、サイズの小さい方の拡張子がjpgで、大きい方がJPG。

!!!!!!。

コードを見ると、小文字の拡張子しか通さないことになってた……orz。

エラーを吐くようにしないとだめぽなり。で、簡単ですが改良してみたのが以下。使っていない変数なんかもありますが、ご参考まで。

<?php

//初期化
$photo = '';

//設定
define('PATH', '/upload/image/');
define('URL', 'http://localhost/upload/');

/*****************************************
 * ファイル名変更
 * てきとーにsha1で変えてみる
 */
function change_filename ($file, $path, $ext)
{
    $rename = sha1(microtime()) . '.' . $ext;
    rename($file, $path . $rename);
    return $rename;
}

/*****************************************
 * サムネイル生成
 */
function create_thumb ($file, $path, $thumb_path, $width)
{
    global $created_file;

    if (preg_match('/[.](jpg)|(jpeg)$/i', $file))
    {
        $image = imagecreatefromjpeg($path . $file);
    }
    elseif (preg_match('/[.](gif)$/i', $file))
    {
        $image = imagecreatefromgif($path . $file);
    }
    elseif (preg_match('/[.](png)$/i', $file))
    {
        $image = imagecreatefrompng($path . $file);
    }

    $ox = imagesx($image);
    $oy = imagesy($image);

    $nx = $width;
    $ny = floor($oy * ($width / $ox));

    $nm = imagecreatetruecolor($nx, $ny);

    imagecopyresampled($nm, $image, 0, 0, 0, 0, $nx, $ny, $ox, $oy);
    if(! file_exists($thumb_path))
    {
        if(! mkdir($thumb_path))
        {
            die('ディレクトリ生成エラーです(^^;)');
        }
    }

    imagejpeg($nm, $thumb_path . $file, 90);

    $created_file = basename($thumb_path . $file);

    imagedestroy($nm);
}

if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
    if (isset($_POST['save']))
    {
        if (isset($_FILES['photo']))
        {
            if (preg_match('/[.](jpg)|(jpeg)|(gif)|(png)$/i', $_FILES['photo']['name']))
            {
                $photo_origin    = $_FILES['photo']['name'];
                $photo_tmp_name  = $_FILES['photo']['tmp_name'];
                $photo_error     = $_FILES['photo']['error'];
                $photo_size      = filesize($photo_tmp_name);
                $photo_pathinfo  = pathinfo($photo_origin);
                $photo_extension = $photo_pathinfo['extension'];

                move_uploaded_file($photo_tmp_name, PATH . 'original/' . $photo_origin);

                $photo_origin = change_filename(PATH . 'original/' . $photo_origin, PATH . 'original/', $photo_extension);

                //サムネイル(小)
                create_thumb($photo_origin, PATH . 'original/', PATH . 'small/', 200);
                $photo_small = $created_file;

                //サムネイル(大)
                create_thumb($photo_origin, PATH . 'original/', PATH . 'large/', 500);
                $photo_large = $created_file;
            }
            else
            {
                $error_msg = 'ファイルの拡張子が変です(^^ゞ)';
            }
        }
    }
}

echo '<?xml version="1.0" encoding="utf-8"?>' . "\n" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">

<head>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <title>画像アップロード・サムネイル生成</title>

</head>

<body>

<h1>画像アップロード・サムネイル生成</h1>

<?php
if (! empty($error_msg))
{
    echo '<p>' . $error_msg . '</p>';
}
?>

<form method="post" action="./" enctype="multipart/form-data">

<p>
<?php if (! empty($photo_small) && ! empty($photo_large)) : ?>

<a href="<?php echo URL . 'image/large/' . $photo_small; ?>" target="_blank">
    <img src="<?php echo URL . 'image/small/' . $photo_small; ?>" alt="" />
</a>

<?php elseif (empty($photo_small)) : ?>

<input type="file" name="photo" value="<?=$photo;?>" />

<?php endif; ?>
</p>

<?php if (empty($photo_small) && empty($photo_large)) : ?>

<p><input type="submit" name="save" value="✓ アップロードする" /></p>

<?php endif; ?>

</form>

</body>

</html>
ポイントを現金でキャッシュバック

Windows XP+VMware Player+Ubuntu Desktopの環境構築 #7 【vi, vimの基本操作】[Permanent Link]

Windows XP+VMware Player+Ubuntu Desktopの環境構築 #6 【Sambaサーバの導入】で、WindowsとVMwareのUbuntuとのフォルダ共有を行いました。

今回はちょっと道を逸れてvi。

ところで、Web制作をデザインから入った人で、Windowsにするか、MacOSにするかで迷っているのはよく見かけますが、WindowsにするかLinuxにするか、あるいはMacOSにするかLinuxにするかで迷っているのは見たことがありません。Linuxの存在を知らない人も見かけます。パソコンと言えばWindowsかMac。今のMacOSはUNIXですが、それでもLinux(BSDとかでもいいのですが)は選択肢の蚊帳の外ですね。LinuxではAdobe製品を使えないのが大きいのでしょうか。あとOffice。というか、ソフトが動けばOSは何でもいいのか。Ubuntuがあるからこれから普及していくだろうかな。でも、デザイナーはwhat you see is what you getなのです! Macの見た目に興味はあっても、中身(UNIX)に興味はないのです(^_^; (きっと)

そんなことはどうでもいいとして、Windowsに慣れてしまった身にはviは辛いです。まず操作がわからん。そこで挫折。よくある。なんとしても操作を覚えなきゃならん。

$ vi

とタイプするとvimが立ち上がる。

$ vim

とタイプしても結果は同じ。

~                              VIM - Vi IMproved
~
~                               version 7.1.138

Ubuntu 8.04ではvimがデフォルトなのですね。

基本のコマンドをメモ。これを覚えなきゃ始まらない。

起動と終了
vi [filename] 開く。
:w 保存する。
:q 終了する。
:wq 保存して終了する。
ZZ 保存して終了する。
:e! 保存前に戻す。
:w! 上書き保存する。
:q! 保存しないで終了する。
:sh シェルを実行する。戻るにはCtrl + D。あるいはCtrl + Zでviを中断。viに戻るにはfg
:pre 強制保存する。
移動
h 左に1文字移動する。2文字移動するには2h
j 下に1文字移動する。2文字移動するには2j
k 上に1文字移動する。2文字移動するには2k
l 右に1文字移動する。2文字移動するには2l
0 行の先頭に移動する。
$ 行の末尾に移動する。
b 前の単語に移動する。2単語前に移動するには2b。句読点を無視するにはB
w 後の単語に移動する。2単語後に移動するには2w
編集
i 挿入する。
a 追加する。
c 変更する。例えば行末まではc$、単語末尾まではcw。など色々組み合わせる。
cc 行全体を変更する。
C カーソル位置から行末までを変更する。c$と同じ。
r 1文字を変更する。
R Escを押すまで変更する(1行まで)。
s 置換する。2文字分は2s
S 行全体を置換する。
~ 大文字と小文字を変更する。
s 置換する。
d 削除する。1語はdw。1語(単語の末尾まで)はde
dd 行全体を削除する。2行削除するには2dd
D カーソル位置から行末までを削除する。
x 1文字削除する。2文字なら2x
y ヤンク。
Y 行全体をヤンク。yyと同じ。
p 配置する。カット&ペーストのペースト。全ての削除を取り消す。
. コマンドを繰り返す。
u 最後のコマンドを取り消す。
U その行を元に戻す。

まだまだあるけれど、ひとまず以上を知っていれば、なんとなく使えた気になります。

$ sudo shutdown -h now
ポイントを現金でキャッシュバック
Copyleft (c) 2008-2010 maaguu.
Email to maaguu:info@mx.maaguu.com Powered by WordPress MU