Latest Publications

CodeIgniterのUploadクラスは画像とその他ファイルを同時に許可できない

全然PHPのネタが無かったので、ちょっとやってみようかと思います。

とある開発でCodeIgniterのUploadクラスを使っていたのですが、
アップロードされたファイルに対して、許可する拡張子を画像+その他拡張子(例えばzipとかtxtとか)に設定すると、

「許可されていない拡張子です」

というエラーがでてアップロードされません。こんなコードで発覚。
CIのバージョンは1.7.1です。

$config['allowed_types'] = ‘gif|jpg|png|zip|txt’;

// その他config設定は省略

$this->load->library(‘upload’, $config);

$this->upload->do_upload();

こう書くと、画像以外のzipファイルをアップロードしたときにはうまくアップロードされません。
でも、$config['allowed_types']の項目は必須設定です。

「う~ん、なんでかなぁ」と思いながらソースを読んでいたら、

system/libraries/Upload.php line:566くらい

// Images get some additional checks
if (in_array($val, $image_types))
{
  if (getimagesize($this->file_temp) === FALSE)
  {
    return FALSE;
  }
}

と、こんな記述を発見。ちなみに$image_typesには画像拡張子の配列が、$valにはallowed_typesで設定した拡張子(|で分割されています)
が入っています。

これだと、allowed_typesの中に画像系の拡張子が入っていると全てgetimagesize()が実行されます。
で、画像ファイル以外なら当然FALSEが返ります。その結果、アップロードは失敗。

これだと困るので、以下にように追記したらうまく動きました。

// Images get some additional checks
// 画像系の拡張子で、かつアップロードされたファイルの拡張子の末尾が画像系拡張子であればチェック
if (in_array($val, $image_types) && strpos(substr($this->file_ext, 0), $val) !== FALSE)
{
  if (getimagesize($this->file_temp) === FALSE)
  {
    return FALSE;
  }
}

 

「strpos(substr($this->file_ext, 0), $val) !== FALSE」

とすることで、アップロードされたファイルの拡張子もチェックし、TRUEならgetimagesize()やってくれ、ってことです。
$this->file_extにはドットを含む拡張子の情報が入っているので、substrで1文字目をカットしてます。

セキュリティ的にどうなのかと言われるとアレですが、そもそも画像以外をアップロードさせるアプリケーションはそれだけリスクが高まるので、仕方無いですかね。

私は恒久的に使うのでコアに追記しましたが、疑り深い方はMY_Upload.phpなどでオーバーライドしたほうがいいかもです。

Subversionのリポジトリツリーを図にするには?

最近、Subversionのリポジトリ管理に頭を悩ませることが多いです。

どうブランチ切ったか? どこからどこまでマージした? このタグはいつのスナップショット?
その時その時はきちんと名前付けやコメント付けをやってるつもりでも、後から見直してみると「ダメだ・・・このリポジトリは腐ってる・・・」とため息をつくことになるのは、僕だけなんでしょうか(笑)

ので、「とりあえず図にしてみればいいんじゃないかな?」と安直に思ったんですが、MS Wordとかで描くのはつまらない。マウスはなるべく触りたくないし、手で図オブジェクトを操作するのは必ずミステークの原因になるのは目に見えてるし。
大体そういうのって、誰かがツール作ってそうだなぁとも思ったんですが、どうもググっても見つからない。うーん。

だんだん考えるのもめんどくさくなってきて、
「とりあえずGraphViz使えばいいんじゃね?」
という結論にたどり着きました。

コードと図のサンプルはこちら。

リポジトリツリー画像サンプル

digraph repositoryTree {
subgraph cluster_trunk {
fillcolor = “#00DDFF”;
style = filled;
label = “trunk”;
r381 [ style = filled ];
r537 [ style = filled ];
r541 [ style = filled ];

r381 -> r537;
r537 -> r541;
}

subgraph cluster_branches {
fillcolor = “#FF8888″;
style = filled;
label = “branches”;
subgraph cluster_dev_ktai {
fillcolor = “#FFFF00″;
style = filled;
label = “dev_ktai”
r382 [ style = filled ];
r538 [ style = filled ];
r542 [ style = filled ];

r382 -> r538;
r538 -> r542;
}
}

r381 -> r382 [ headlabel = "ブランチ作成" fontsize = 8 fontcolor = "#FF0000" ];
r537 -> r538 [ headlabel = "merge" fontsize = 8 fontcolor = "#0000FF" ];
r541 -> r542 [ headlabel = "merge" fontsize = 8 fontcolor = "#0000FF" ];
}

おっ、意外と見やすい・・・と思う。うん。

個人的にはこれで満足。dotファイル書くのは面倒かもしれないけど、ある程度下地できたらコピペでOKだし。
ちなみに、もうちょっと大きい例はこんな感じ。(ウチのとあるリポジトリから。気まずい部分は黒く塗りつぶしてます)

ちょっとゴチャついてますが、慣れるとどのブランチがどれにマージされてるかなどが1〜2ステップで分かるようになります。
結構いいと思うんだけど、どうかなー。

あと頑張れば自動生成できそうな予感。誰か作ってないかなぁ。

ものすごく意味のないコード

jQueryのこと書いてたら思いついた。

既出かな?

$(function(){

alert($($($($($($($(‘#box’).get()).get()).get()).get()).get()).get()));

});

さて、結果はHTML Elementでしょうか?jQueryオブジェクトでしょうか?

答え:どうでもいい(笑)

一応、jQueryオブジェクトです。

7バイトでIEかどうかを判定する方法

少し前に、JScriptの条件付きコンパイルを使ってIEを判定する方法が流行りました。

var IE = /*@cc_on!@*/false;

とかやる方法ですね。

ただ、条件付きコンパイルは遅くなるだとか(信憑性は不明ですが)聞いたので使ってませんでした。

で、タイトルの方法。こう書くそうです。

var IE = !+”\v1″;

これで、IEはtrue,それ以外はfalseとなります(Opera,Safari,Firefox,Chromeで検証)。

swfObject.jsのソースを読んでたらこんなことしてるもんだから、もとネタを見たらちゃんと解説もしてありました。ちょろっと見たことを書いてみます。

→もとネタ http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html

どうやら、\vは通常のブラウザはvertical space(改行つきスペース?)の文字として認識するようで、

var IE = !(+

1

)

と認識するようですね。で、1の否定で0だからfalse。うん、納得。

しかし、IEは\vは文字列として認識するようで、

var IE = !(+v)

カッコ内の結果をisNaNしてtrueになるようです。

こんなのよく考えたなぁ、と関心しながら自分のフレームワークに取り込みました。

indexOfでuserAgent判定してたから、その分早くなったかなぁ。

・・・でもこれってJScriptの解釈のせいなのかな?IE9からできなかったりして。

そうなったら元に戻すしかないね。

セレクタ検索系でIEが遅くなる理由

セレクタ検索を独自に実装されている方は多いと思いますが、どうにもIE(特にIE6、7)が遅いと感じることはありませんか?

もちろん、JScriptのエンジン自体が遅いからなのですが、それ以外にもちょっとしたことで速度が改善したりします。

特に私が感じたのは、「NodeList」のループの部分です。

var list = document.getElementsByTagName(‘p’);

for (var i = 0; i < list.length; i++) {

// do something

};

と書くことが多いかと思います。しかし、これは遅いです。

var list = document.getElementsByTagName(‘p’), len = list.length;

for (var i = 0; i < len; i++) {

// do something

};

これだと数倍早く動きます。600回くらいのループで検証しました(内部処理は++countくらいで)が、

前者はIE6で約500ms、後者だと20数msでループが完了しました。

NodeListはSnapShotではなくLiveなものだから、ループの際に逐一「.length」の変化を見てるのかな?

もしくはNodeList.lengthのアクセス自体が遅いのか。

どちらかはわかりませんが、とにかくループ前にNodeListの長さを変数に格納してループすると

早くなるようです。

「数msくらい気にしない」、もしくは「IEはこんなもん」と思うのならさほど気にしなくてもよさそうですが、検索が遅いとその後の処理も遅くなってしまいますよね。

※どうもセレクタ検索を書くと速度のチューニングにハマって仕事にならないですね。

とりあえずトータルではjQueryより早ければいいかな。