前回の続きです。プラグインを作成しましたが、挙動が変わらなかったためさらに調査を続行しました。
結果
結果から記すと、プラグインを以下のように改修することで解決できました。
<?php
/*
Plugin Name: MIMEuruwashii
Description: PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策
Version: 0.0.3
Author: アルム=バンド
*/
/**
* MIMEuruwashii : PHP の MIME タイプ判定で特定の .xlsファイル が弾かれる現象への対策
*/
class MIMEuruwashii
{
/**
* __construct : コンストラクタ
*
*/
public function __construct() {
add_filter(
'wp_check_filetype_and_ext',
[
$this,
'Bibishii',
],
99,
3
);
}
/**
* Vivid : 追加する MIMEタイプ の定義
*
* @return {Object} : 誤判定している MIME タイプを追記する
*
*/
public static function Vivid(){
return [
[
'xla|xls|xlt|xlw' => 'application/vnd.ms-office'
],
[
'xla|xls|xlt|xlw' => 'application/vnd.ms-excel'
],
];
}
/**
* Bibishii : フィルター追加
*
* @param {Object} $check : MIMEタイプ の一覧のオブジェクト
* @param {File} $file : チェック対象ファイル
* @param {Object} $filename : チェック対象ファイルの名前
*
* @return {Object} : 誤判定している MIME タイプを追記する
*
*/
public function Bibishii ( $check, $file, $filename )
{
if ( empty( $check['ext'] ) && empty( $check['type'] ) ) {
foreach ( self::Vivid() as $mime ) {
remove_filter(
'wp_check_filetype_and_ext',
[
$this,
'Bibishii'
],
99
);
$mime_filter = function($mimes) use ($mime) {
return array_merge($mimes, $mime);
};
add_filter(
'upload_mimes',
$mime_filter,
99
);
$check = wp_check_filetype_and_ext( $file, $filename, $mime );
remove_filter(
'upload_mimes',
$mime_filter,
99
);
add_filter(
'wp_check_filetype_and_ext',
[
$this,
'Bibishii'
],
99,
3
);
if ( ! empty( $check['ext'] ) || ! empty( $check['type'] ) ) {
return $check;
}
}
}
return $check;
}
}
// instantiate
$ab_wp_plugin_mimeuruwashii = new MIMEuruwashii();
元の原型ないですね。引っかけるアクションフックも upload_mimes
から wp_check_filetype_and_ext
に変わっています。
調査
調査の過程を以下に記します。
挙動が変わらなかった理由を wp-includes/functions.php
を追いかけて調べて行きます。
- メソッド
wp_get_mime_types()
では拡張子と MIMEタイプ をそれぞれキー・値として持つ連想配列を返却しています wp_get_mime_types()
が呼ばれている場所を辿っていきますget_allowed_mime_types()
はいくつかの処理(.swf
や.exe
の除外、ユーザ権限がない場合は.html
,.htm
,.js
を除外)をした後にupload_mimes
でチェックする拡張子と MIMEタイプ を登録do_enclose()
は動画関連のチェックのように見えます。しかし、ここで気になるコードを発見- 拡張子の方は
if ( preg_match( '!^(' . $exts . ')$!i', $extension ) )
で正規表現による判定を行っている (例えば!^(xla|xls|xlt|xlw)$!i
のような形式) - 一方、 MIMEタイプ は
$type = $mime;
で代入しているだけ- しかも
break;
している
- しかも
- 拡張子の方は
上述のコードを見て、「ひょっとして複数の MIMEタイプ を想定していないのでは?」と思い至り、検索。
やはり1つの拡張子に対して複数の MIMEタイプ を持つ場合に対応していない (今回のケースで言うと「 .xls
ファイル は application/vnd.ms-excel
または application/vnd.ms-office
を受け付ける」としたい) ようです。
前回のように MIMEタイプ を付け足す方法は、 .ai
や .psd
のような、デフォルトの連想配列に拡張子がキーとして存在しない MIMEタイプ ならば良いと思います。しかし、今回の .xls
のように既存の拡張子の場合は最後に付け足したキーまで評価されていなさそうです。
そこで、上述記事のように前回の upload_mimes
時に判定する MIMEタイプ を付け足すのではなく、そもそものチェック処理の wp_check_filetype_and_ext
を変更する、という方針に転換しました。
その結果が冒頭のプラグインのコードとなります。
雑感
複数の MIMEタイプ が想定されていない作りということを初めて知って驚きました。
上述の Stackoverflow でも取り上げられていますが、 .svg
や .zip
等他にも類似のケースが考えられるので意外でした。