2012年1月22日日曜日

array_merge()と演算子「+」

phpで配列を結合する関数がある。「array_merge()」だ。

よく使うパターンはこんなシーン。

$a = array_merge($a, $b);

$aと$bが連想配列なら同じことを

$a = $b+$a;

と書ける。
足し算だが「+」演算子の左右の変数を入れ替えると結果が変わってしまう。
共通のキーがあった場合に、上書きをしないからだ。

$a=array(
 'x'=>'aX',
 'y'=>'aY',
);
$b=array(
 'y'=>'bY',
 'z'=>'bZ',
);

だった場合、
$a+$b
は
array(
 'x'=>'aX',
 'y'=>'aY',
 'z'=>'bZ',
);
となり、
$b+$a
は
array(
 'x'=>'aX',
 'y'=>'bY',
 'z'=>'bZ',
);
になる

$aと$bに共通のキーがないか、共通だった場合、$aを採用するならば
$a += $b;
と書くことができる。

連想配列の変数名は長めになることが多いのでこの書き方ができればありがたいが、
ここまで短く書けるシーンにあまりであったことがないのが残念。
でもこうして整理してみると、パラメータ$aをデフォルト値$bで補完する場合に使えそうだなあ。

$a, $bが通常の整数をキーに持つ普通の配列だった場合、「+」演算子の挙動はarray_mergeの挙動と著しく違うので知らないととまどう。
array('a1','a2','a3')+array('b1','b2','b3')
の結果はどうなるか?

array('a1','a2','a3')
となる

2012年1月18日水曜日

Bloggerでコード整形を行う


Bloggerに張っているソースコードやコマンドの実行結果等が見にくいので、下記のブログを参考にデザインを変更してみた

seiji's 十八番: [SyntaxHighlighter] bloggerに整形済みテキスト、プログラムソースコードなど...

SyntaxHighlitghterは以前、ExtJSでSVNビューワを作ったときに使ったことがあったので久しぶりに使ってみた。

やることは、上記のリンクに書いてある通り。ただ、使用するブラシの種類が違っていたのでそこだけ、選択を変えた。
現在使っていないプログラミング言語はばっさり捨て、テキスト用に「Plain」をdiff用に「Diff」を追加した。「Xml」は外しかけたが、htmlを整形するために必要だった。

差し込んだコードは以下の通り。

<!--ここからsyntaxHighlighterをコピペした-->
<link type='text/css' rel='stylesheet' href='http://alexgorbatchev.com/pub/sh/current/styles/shCore.css' />
<link type='text/css' rel='stylesheet' href='http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDiff.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' />
<script type='text/javascript' src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' />
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.all();
</script>
ついでにメインのカラムの横幅を少し広げて表示できる桁数を増やした
-      <b:variable default='960px' name='content.width' type='length' value='860px'/>
----
+      <b:variable default='960px' name='content.width' type='length' value='1000px'/>

2011年11月8日火曜日

strftimeで日付がずれる

もしかしたら有名な話なのかもしれませんが、今日 スケジュールの画面のデモをphpで作っていたときにちょっとはまった。 クライアントにイメージをつかんでもらうための簡単なカレンダを phpでループさせて作っていたのだが、デモプログムの一部で、
date_default_timezone_set('Asia/Tokyo');
$base = strtotime('2011-11-01');
$days = explode(',', '日,月,火,水,木,金,土');
for ($i=0; $i<30; ++$i) {
 $date = $base+24*60*60*$i;
 $mmdd = strftime('%m/%d', $date);
 $day = $days[strftime('%w', $date)];
 echo "<br>$i: $mmdd($day)";
}

みたいに、今月の頭からループで24時間づつ増やしながら日付を表示させて、カレンダの日付の部分を表示させようとしたら、想定外の出力に!

0: 11/01(火)
1: 11/02(水)
2: 11/03(木)
3: 11/04(金)
4: 11/05(土)
5: 11/06(日)
6: 11/06(日)

7: 11/07(月)
8: 11/08(火)
9: 11/09(水)
10: 11/10(木)
: 
打ち合わせ開始まで30分しかなかったので、軽いパニックに。
誤差が蓄積されたような動きであったので、とりあえず、その場は
$base = strtotime('2011-11-01 14:00');
変更したところ、想定の出力で動き、残りの部分のコーディングに戻ることができた。

気になったので、今、「夏時間」をwikipediaで調べたら、


アメリカ合衆国(一部除く。前述のとおり2007年から次のように変更され実行されている)、カナダ(一部除く)、メキシコ(一部除く) - 3月第2日曜日午前2時〜11月第1日曜日午前2時(現地時間基準。開始日には2時が3時になり(1時59分59秒の次が3時00分00秒)、終了日は2時が再度1時(1時59分59秒の次が1時00分00秒)になるため、開始日は1日が23時間、終了日は逆に25時間になる)
と、アメリカでは11/6は25時間あることがわかった。ちなみに前のスクリプトに時間まで表示してみると、
0: 11/01 00:00(火)
1: 11/02 00:00(水)
2: 11/03 00:00(木)
3: 11/04 00:00(金)
4: 11/05 00:00(土)
5: 11/06 00:00(日)
6: 11/06 23:00(日)

7: 11/07 23:00(月)
8: 11/08 23:00(火)
9: 11/09 23:00(水)
10: 11/10 23:00(木)
11/6の00:00に24時間を足すと、11/6の23:00になっている。


ああ、すっきり!!、でもさ、夏時間のない'Asia/Tokyo'のロケールのはずじゃん。まいっか。いや、よくないな、よくない。
ここで、デジャヴが、どっかの環境で'Asia/Tokyo'が効かなくて、GMT-9って指定したことあったっけ。

 date_default_timezone_set('Etc/GMT-9');
 $base = strtotime('2011-11-01');
 $days = explode(',', '日,月,火,水,木,金,土');
 for ($i=0; $i<30; ++$i) {
  $date = $base+24*60*60*$i;
  $mmdd = strftime('%m/%d  %H:%M', $date);
  $day = $days[strftime('%w', $date)];
  echo "<br>$i: $mmdd($day)";
 }
 結果は
0: 11/01 00:00(火)
1: 11/02 00:00(水)
2: 11/03 00:00(木)
3: 11/04 00:00(金)
4: 11/05 00:00(土)
5: 11/06 00:00(日)
6: 11/07 00:00(月)

7: 11/08 00:00(火)
8: 11/09 00:00(水)
9: 11/10 00:00(木)
10: 11/11 00:00(金)
と、よしっ!きれいだ。ってなんでAsia/Tokyoと結果が違うの?
あ、そっか。日本って震災の節電の関係で夏時間設定されたんだっけ。
なんか違う。眠いからに違いない。寝よう。

よし、リフレッシュ。

ソースを少し変えてタイムゾーンを変えて流せるようにした。

echo "<pre>\n";
printDays('UCT');
printDays('Etc/GMT-9');
printDays('Asia/Tokyo');
printDays('America/New_York'); 
function printDays($timezone) {
echo "timezone:$timezone\n";
date_default_timezone_set($timezone);
$base = strtotime('2011-11-01');
for ($i=4; $i<8; ++$i) {
$date = $base+24*60*60*$i;
$mmdd = strftime('%m/%d  %H:%M', $date);
echo "$i: $mmdd\n";
}
echo "base:$base\n";
echo "now:".strftime('%Y-%m-%d %H:%M:%S')."\n\n";
}
結果はこんな感じ
timezone:UCT
4: 11/05 00:00
5: 11/06 00:00
6: 11/07 00:00
7: 11/08 00:00
base:1320105600
now:2011-11-08 23:50:43 
timezone:Etc/GMT-9
4: 11/05 00:00
5: 11/06 00:00
6: 11/07 00:00
7: 11/08 00:00
base:1320073200
now:2011-11-09 08:50:43 
timezone:Asia/Tokyo
4: 11/05 00:00
5: 11/06 00:00
6: 11/06 23:00
7: 11/07 23:00
base:1320120000
now:2011-11-08 18:50:43 
timezone:America/New_York
4: 11/05 00:00
5: 11/06 00:00
6: 11/06 23:00
7: 11/07 23:00
base:1320120000 
now:2011-11-08 18:50:43
そうそう、 日本は震災でアメリカに..そんなわけない。
使っているサーバのタイムゾーンのデータが壊れているようだ。

ローカルのphp5.3.3の環境では、timezone:Asia/Tokyoの表示は
timezone:Etc/GMT-9と同じ結果になっている。
不思議な動作をするサーバはphp5.3.1。
業務で使うサーバもほぼ同じ環境だから確認しておかなくては。

2011年9月8日木曜日

mySQLの CASE WHENはSQLが長くなる

mySQLの CASE WHENはSQLが長くなるので、短く書く方法を探ってみる。

mySQLのマニュアル比較演算子の冒頭に
「比較演算は、1(TRUE)、0(FALSE)、または NULL の値を返します。」
と書かれている。

なので、下記4つのSQLはすべて同じ結果になる。(はずw。ただし列Cは数値型だとする)

SELECT CASE WHEN `a`=`b` THEN `c` ELSE 0 END FROM `abc`;
SELECT CASE `a` WHEN `b` THEN `c` ELSE 0 END FROM `abc`;
SELECT IF(`a`=`b`,`c`,0) FROM `abc`;
SELECT (`a`=`b`)*`c` FROM `abc`

お好みはどれでしょう。

2011年5月11日水曜日

mySQLについてのメモ

mySQLのINTのMAX値

  • Unsigned で、4G(40億)

mySQLでの正規表現の利用

  • 正規表現は where `name` REGEXP 'a*' のように使用する

mySQLのvarchar、textの比較について

  • インデックスを張る場合255文字以内のキー長を指定する必要がある

他のDBのテーブルを参照するには

  • 同じmySQLサーバ内なら、select * from db_name.table_nameでOK

他のDBのテーブルとJOINできるか

  • 下記のように普通にJOINできる
    SELECT * FROM `mytable` join `dbname`.`atable` A on `mytable`.`id`=A.`id`

synonymはあるか

  • 無い。
    viewを使えば同様のことができる。オーバーヘッドが問題にならないか注意して使う

日付時刻を丸める関数(TRUNCなど)はあるか

  • 日付丸め専用の関数はない。
  • 文字列にフォーマットして丸める例はよく見かける
  • 計算式で丸める例
     時間:DATE(`date`)+INTERVAL HOUR(`date`) HOUR
     日: DATE(`date`)
     週: DATE(`date`)-INTERVAL DAYOFWEEK(`date`)-1 DAY
     月: DATE(`date`)-INTERVAL DAYOFMONTH(`date`)-1 DAY

PostgreSqlにある「関数(式)インデックス」はあるか

  • 無い。→インデックスを使いたい式を展開した列が必要

タイムゾーンはDBもしくは接続毎に再指定可能か
  • 可能
    mysql> set time_zone = '-7:00';
    Query OK, 0 rows affected (0.00 sec)

    mysql> select now();
    +---------------------+
    | now() |
    +---------------------+
    | 2011-05-05 22:21:37 |
    +---------------------+
    1 row in set (0.00 sec)

    mysql> set time_zone='+9:00' ;
    Query OK, 0 rows affected (0.00 sec)

    mysql> select now();
    +---------------------+
    | now() |
    +---------------------+
    | 2011-05-06 14:22:01 |
    +---------------------+
    1 row in set (0.00 sec)

    2011年4月21日木曜日

    Ext4.0ドキュメントの印刷

    Ext4.0のドキュメントページを印刷するブックマークレットを作ってみた。

    今回は隠したインラインフレームに作成して、そのまま印刷するのではなく、
    印刷可能なWindowを表示するようにした。ExtWikiでも試行錯誤したが、プレビュー代わりになりこれが一番便利だった。
    また、ガイドがマニュアルに統合されたので、こちらも印刷しやすいようにしてみる。

    例によってブックマークレット表示サイトで表示してみる。
    ExtJS4.0 ドキュメント印刷(クラス)

    ExtJS4.0 ドキュメント印刷(ガイド)

    もともとβ3の時に作成したが、ファイナルで少し変わっていたので上記に修正した。β3の時のものはこんな感じ
    ExtJS4.0β3 ドキュメント印刷(クラス)

    2011年4月3日日曜日

    画像をphpスクリプトのみで表示する

    画像のパスが問題で、プラグインやライブラリの設計や導入方法が若干複雑になる事がある。
    こんなとき、画像をプログラムに取り込んでしまうと設定自体不要になるケースがある。

    そのやり方をメモしておく
    1.画像をbase64文字列に変換する
    2.変換したbase64文字列をプログラムに組み込む

    1.画像をbase64文字列に変換する
     次のようなプログラムでbase64文字列を作成できる
    img_encode.php
    
    <?php
      //クォート内はターゲットのファイル名
      die(base64_encode(file_get_contents(''));
    ?>
    

    出力は下記のようになる

    R0lGODlhFQATAPcAAP//////zP//mf//Zv//M///AP/M///MzP/Mmf/MZv/MM//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9mZv9mM/9mAP8z//8zzP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8AM/8AAMz//8z/zMz/mcz/Zsz/M8z/AMzM/8zMzMzMmczMZszMM8zMAMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xmzMxmmcxmZsxmM8xmAMwz/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswAM8wAAJn//5n/zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZzJmZmZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkzZpkzM5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/M2b/AGbM/2bMzGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZAGZm/2ZmzGZmmWZmZmZmM2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA/2YAzGYAmWYAZmYAM2YAADP//zP/zDP/mTP/ZjP/MzP/ADPM/zPMzDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZMzOZADNm/zNmzDNmmTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMAzDMAmTMAZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDMMwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBmAAAz/wAzzAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAMwAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAFQATAAAI/AABCBzoaRQpUVSopFJFaqDDgXny/JH4B8+fUchUOVP1UOCTKhEjjho5KlUqUqpGPXxSoKVLPwmp+CnZ0s/AJypaOqmCJ48fPFScUBGVqmVKgSRaqkhosSWVmi4LOAPgpkTUlkBdPjXqzBmpEjkLqFAhNKFSoUZNkkoqduxSJy7H1iw5ShXbq1iVbi0gCtVXt0up4PnpByYVskz9kKIz1okKnnkGQy0Aw2wBZCUcF6iy2elMP1H9iPLzCcAczqEnF8ha4I/AEnudthTlEtVWTwOtuqS9enZUPQ9LzASacKloVKj8dOoIoESehGXLCgbOXCAmT53++JmYB/fDgAA7

    2.変換した文字列をプログラムに組み込む
     上記で変換して得られたbase64文字列をbase64_decodeしヘッダーとともに出力する。
     1行が長い場合、適当な長さに切り、使うときに文字列として連結する。
    img_decode.php
    
    <?php
      $img = 'R0lGODlhFQATAPcAAP//////zP//mf//Zv//M///AP/M///MzP/Mmf/MZv/M'
     .'M//MAP+Z//+ZzP+Zmf+ZZv+ZM/+ZAP9m//9mzP9mmf9mZv9mM/9mAP8z//8z'
     .'zP8zmf8zZv8zM/8zAP8A//8AzP8Amf8AZv8AM/8AAMz//8z/zMz/mcz/Zsz/'
     .'M8z/AMzM/8zMzMzMmczMZszMM8zMAMyZ/8yZzMyZmcyZZsyZM8yZAMxm/8xm'
     .'zMxmmcxmZsxmM8xmAMwz/8wzzMwzmcwzZswzM8wzAMwA/8wAzMwAmcwAZswA'
     .'M8wAAJn//5n/zJn/mZn/Zpn/M5n/AJnM/5nMzJnMmZnMZpnMM5nMAJmZ/5mZ'
     .'zJmZmZmZZpmZM5mZAJlm/5lmzJlmmZlmZplmM5lmAJkz/5kzzJkzmZkzZpkz'
     .'M5kzAJkA/5kAzJkAmZkAZpkAM5kAAGb//2b/zGb/mWb/Zmb/M2b/AGbM/2bM'
     .'zGbMmWbMZmbMM2bMAGaZ/2aZzGaZmWaZZmaZM2aZAGZm/2ZmzGZmmWZmZmZm'
     .'M2ZmAGYz/2YzzGYzmWYzZmYzM2YzAGYA/2YAzGYAmWYAZmYAM2YAADP//zP/'
     .'zDP/mTP/ZjP/MzP/ADPM/zPMzDPMmTPMZjPMMzPMADOZ/zOZzDOZmTOZZjOZ'
     .'MzOZADNm/zNmzDNmmTNmZjNmMzNmADMz/zMzzDMzmTMzZjMzMzMzADMA/zMA'
     .'zDMAmTMAZjMAMzMAAAD//wD/zAD/mQD/ZgD/MwD/AADM/wDMzADMmQDMZgDM'
     .'MwDMAACZ/wCZzACZmQCZZgCZMwCZAABm/wBmzABmmQBmZgBmMwBmAAAz/wAz'
     .'zAAzmQAzZgAzMwAzAAAA/wAAzAAAmQAAZgAAMwAAAP///wAAAAAAAAAAAAAA'
     .'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
     .'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
     .'AAAAAAAAAAAAAAAAAAAAACwAAAAAFQATAAAI/AABCBzoaRQpUVSopFJFaqDD'
     .'gXny/JH4B8+fUchUOVP1UOCTKhEjjho5KlUqUqpGPXxSoKVLPwmp+CnZ0s/A'
     .'JypaOqmCJ48fPFScUBGVqmVKgSRaqkhosSWVmi4LOAPgpkTUlkBdPjXqzBmp'
     .'EjkLqFAhNKFSoUZNkkoqduxSJy7H1iw5ShXbq1iVbi0gCtVXt0up4PnpByYV'
     .'skz9kKIz1okKnnkGQy0Aw2wBZCUcF6iy2elMP1H9iPLzCcAczqEnF8ha4I/A'
     .'EnudthTlEtVWTwOtuqS9enZUPQ9LzASacKloVKj8dOoIoESehGXLCgbOXCAm'
     .'T53++JmYB/fDgAA7';
      header('Content-Type: image/gif');
      die(base64_decode($img));
    ?>
    

    apacheのmod_rewriteで%2fを変換

    apacheは通常エンコードされたパス分離文字(%2F)が存在するURLを404(Not found)エラーで拒否する。

    通常は問題にならないのだろうが、パラメータをURLに埋め込み、mod_rewiteでphpに処理させるとき、urlやpathのようなパラメータを扱おうとすると顕在化する。

    apacheにこれを許可させるには、apacheのhttpd.confに「AllowEncodedSlashes On」を記述すればよい。

    VartualHostディレクティブ内でもOKだが、.htaccessに記述するとエラーとなる。

    参考
    apacheマニュアル
    PHPプロ!mod_rewriteでの%2F問題

    2011年3月24日木曜日

    ExtJSのStoreにデータダウンロードの機能を追加する PART2

    前回、時間の無い中作った「ExtJSのGridやChartにデータダウンロードの機能を追加する」を利用したアプリケーションで、追加要望が出たので拡張について少し考えてみた。

    前回、拡張の方法としてはGridに特化して、Gridで表示されているカラムやrendererを利用して、見えているものが、タブ区切りでダウンロードできる事を検討した。が、見えているものとは別に列やフォーマットを指定できる方が汎用性が高いことが分かり、その方向で拡張を実施することとした。

    拡張したdownloadメソッド
     /**
      * Download store data
      * @param {Object} opt (optional) Specifies download filename and columns.
      */
     Ext.data.Store.prototype.download = function(opt){
      opt = opt||{};
      var output=[],o=[],
       columns = opt.columns||(function(st){
        var cs=[], i=0;
        st.fields.eachKey(function(k) {cs.push(k);});
        return cs;
       })(this),
       fn = opt.filename||opt.fn||'download.txt';
      for (var i=0,col; col=columns[i]; ++i) {
       if (typeof col === 'string') {
        columns[i] = col = {field: col}
       }
       if (!col.header) col.header = col.field;
       if (!col.format) col.format = function(v,r){return v;};
       o.push(col.header);
      }
      output.push(o.join('\t'));
      this.each(function(rec) {
       var o=[];
       for (var i=0,col; col=columns[i]; ++i) {
        o.push(col.format(rec.get(col.field), rec));
       }
       output.push(o.join('\t'));
      });
      var form = Ext.DomHelper.append(document.body, {
       tag: 'form',
       style: 'display:none',
       action: 'download.php',
       method: 'post',
       cn:[{
        tag:'textarea',
        name:'body',
        html:output.join('\n')
       },{
        tag:'input',
        name:'Content-Disposition',
        value:'attachment;filename='+'"'+fn+'"'
       },{
        tag:'input',
        name:'Content-Type',
        value:'text/tab-separated-values'
       }]
      });
      form.submit();
      document.body.removeChild(form);
     }
    

    呼出しサンプル1
    こちらではパラメータなしでdownloadメソッドを呼び出した例、storeの内容がそのままダウンロードされる

    呼出しサンプル2
    こちらはパラメータとして、ダウンロードファイル名や、列やフォーマットの指定を行ってダウンロードする例

    使用に当たっては「ExtJSのGridやChartにデータダウンロードの機能を追加する」で使用したdownload.phpが必要となります。

    phpの出力をブラウザやプロキシーにキャッシュさせない

    phpのヘッダーのマニュアル内にそのものが載っているが、下記を設定するとかなりキャッシュされにくくなるとのこと。

        header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // 過去の日付
    

    ちなみに少し以前のマニュアルの例では、もう少しコテコテやっていた。

        // 日付が過去
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        // 常に修正されている
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        // HTTP/1.1
        header("Cache-Control: no-store, no-cache, must-revalidate");
        header("Cache-Control: post-check=0, pre-check=0", false);
        // HTTP/1.0
        header("Pragma: no-cache");
    

    2011年3月11日金曜日

    phpで仮名を全角に、英数字と空白を半角に統一する(mb_convert_kana)

    何と1行でできる

    mb_convert_kanaを使用する

    /*「半角カタカナ」を「全角カタカナ」に変換し、
     「全角」英数字を「半角」に変換します。
      オプションの意味
       K: かな半→全
       V: 濁点をマージ
       a: 英数字を半角にする
       s: スペースを半角にする(U+3000 -> U+0020)
     */
    
    $str = mb_convert_kana($str, 'KVas', 'utf-8');
    

    エンコードをEUCもしくはUTF-8とすれば、ついでに英数字の後に続く長音記号をハイフンに変換するのも簡単。

    $str = mb_convert_kana($str, 'KVas', 'utf-8');
    $str = preg_replace('/([a-z0-9])ー/i', '${1}-', $str);
    

    2011年3月9日水曜日

    Illuminations for Devellopers 1.1.5導入

    ExtJSでの開発やデバッグの効率を桁違いに高めてくれるかも知れないツールがFireFoxのアドオンとして提供されている。

    名前は「Illuminations for Devellopers」で今日現在のバージョンは1.1.5となっている。Senchaのブログに載っていたのでインストールしてみた。

    インストールは通常のFirefoxのアドオンと同じ。但し、アドオンFirebugが導入されていることは前提となります。
    「ツール」→「アドオン」メニューから、「アドオンを入手」をクリックし、検索のテキストボックスに「Illuminations」と入力し検索すると
    Illuminations for Developers for Firebug

    作成者: Steven Roussey
    が、見つかるのでこれを「Firefoxに追加」する。インストール完了後にFirefoxを再起動し、Firebugを開くと、一番右「接続」の隣に「Illuminations[Trial]」のタブが増えていることがわかる。赤い大きな「Buy or Login」のボタンが少々いやらしいが、まずは無視。

    早速、ExtJSを使用しているページを見に行くと、ViewPort以下どのようにBorderLayoutがあり、その中にある入れ子のBorderLayoutがTabPanelでありどんなメニューとプロパティを持っているかなど、階層的に確認していくことができる。


    今まで、FireFoxでこれを確認していくことはスクリプトのウォッチの機能でできないことはなかったが、いろいろな情報が頭に入っており、見たいデータを明確にして探さないと見つけることはできなかったし、探したオブジェクトがどんなクラスのオブジェクトかということですら、持っているプロパティやメソッドから推測することしかできなかった。

    このアドオンの利用によりかなりデバッグの敷居が下がるかと思う。
    まだ、全然見切れていないが、導入後気になったのはこのアドオンがトライアルで、製品版があるらしいということ。どんな機能の違いがあるのだろう。

    2011年3月1日火曜日

    ExtJSのGridやChartにデータダウンロードの機能を追加する

    ExtJSのGridやChartにデータダウンロードの機能を追加した際のメモ

    ダウンロードといえば本来サーバ側に実装する機能だが、超短納期で片づけたかったため時間のかからなそうな、javascript側のStoreからダウンロードする方法を検討した。

    作成したサンプルは以下の2つ。いずれもExtJSのサンプルに、ダウンロード機能を呼び出すためのボタンを追加したものとなる。

    Chartのほうはサーバに無いデータを表示する例なので、よりStoreからのダウンロードに意味が出るかと思う。



    ダウンロードはクライアント側だけでは仕組み上実現できないので、ブラウザからPOSTされたデータをそのまま出力する鏡みたいなphpスクリプトを使用することにした。このあたり、javascriptだけでできる方法があるなら、是非コメントでアドバイスいただきたい。

    用意したphpスクリプトdownload.phpは以下のようなものだ。
    本来、3行で実装できるが、ヘッダの自由度を持たせるためと、最低限のセキュリティとして、リファラが同じサーバかだけのチェックを入れている。

    <?php
     if (!isSet($_SERVER['HTTP_REFERER']) 
      || !preg_match('#^http://([^:/]+)#',$_SERVER['HTTP_REFERER'],$mch)
      || $mch[1]!==$_SERVER['SERVER_NAME']
     ) die('Security Error'); // simple security check
     foreach ($_POST as $header=>$val) {
      if ($header==='body') continue;
      header("$header: $val");
     }
     die($_POST['body']);
    ?>
    

    ChartやGridPanelのスクリプトの変更方法は、ツールバーやツールボタンで、下記のStoreに追加したメソッドExt.data.Store::download(fn)を呼び出すというもの、StoreはGridやChartで使用しているものを、引数fnはダウンロードの際のファイル名を指定すればよい。

    Ext.data.Store.prototype.download = function(fn){
        var output=[],o=[],
          fld = this.fields;
        fld.eachKey(function(key){o.push(key)});
        output.push(o.join('\t'));
        this.each(function(rec) {
          var o=[];
          fld.eachKey(function(key){o.push(rec.get(key))});
          output.push(o.join('\t'));
        });
        var form = Ext.DomHelper.append(document.body, {
          tag: 'form',
          style: 'display:none',
          action: 'download.php',
          method: 'post',
          cn:[{
            tag:'textarea',
            name:'body',
            html:output.join('\n')
          },{
            tag:'input',
            name:'Content-Disposition',
            value:'attachment;filename='+'"'+fn+'"'
          },{
            tag:'input',
            name:'Content-Type',
            value:'text/tab-separated-values'
          }]
        });
        form.submit();
        document.body.removeChild(form);
      }
    
    

    今回の方式はStoreにfilterがかかっていれば、表示されているレコードのみDLされる。
    Grid専用にするならば、これを一歩進め、ユーザが画面で操作した列の順番や列の表示/非表示を反映したり、列によってはrendererを指定できるようにすると、ユーザが見たままを手に入れるという意味で完成形になるのではと思う。

    今回の例だと出力したLastUpdateの列のDateの出力形式は、欧米人がみればわかりやすいが、日本人向けでなく、Excelでそのまま処理できない等の不満はある。

    英語版のフォーラムに投稿してみました
    http://www.sencha.com/forum/showthread.php?125611-data-download-function-from-Grid-and-Chart

    2011年2月15日火曜日

    ExtJS Pivot Gridの拡張

    ExtJS3.3のPivotGridは、ヘッダのrendererは無く、セルのrendererのパラメータが値しか受け取れない仕様となっている。今回初めて使用したが、必要に迫られてHackしたのでメモしておく。

    それぞれ下記に数行のコードを修正、追加すればよい
    ・ヘッダのrendererの実装: Ext.grid.PivotAxsisのbuildHeaders()メソッド
       行列のそれぞれ各ディメンジョンごとに指定可能
    ・セルのrendererの拡張: Ext.grid.GridViewのrenderRows()メソッド
       値以外に、行のカウンタ、列のカウンタ、行ヘッダの情報、列ヘッダの情報、
       グリッドの参照を受け取れるようにした

    実装結果はこちら

    2011年2月10日木曜日

    ExtJS pivotのサンプル

    ExtJS pivotのサンプルは下記に紹介されているが、残念なことにリンクが切れている

    Ext JS 3.3 Beta 2 がリリース

    サンプルは別URLに残っているのでリンクをあげておく

    サンプル1
     http://dev.sencha.com/deploy/dev/examples/pivotgrid/simple.html
    サンプル2
     http://dev.sencha.com/deploy/dev/examples/pivotgrid/people.html
    サンプル3
     http://dev.sencha.com/deploy/dev/examples/pivotgrid/countries.html

    ExtJSで折れ線グラフを出したときの線のスタイル

    ExtJSのチャート(Ext.chart)は簡単にグラフを表示できるのだが、
    マニュアルに何が指定できるのか説明がほとんどない。

    デコンパイラでcharts.swfの内部を調べて何ができるのか少し調べてみた。
    ほんとは動的にseriesを増減させるメソッドが無いか調べていたのだが
    それらしいものは無かった。
    代わりにseriesで指定できそうなstyleのプロパティを見つけたのでメモしておく
    ※visibilityでおおっと思ったがpi chart専用だった

    {
     height:350,
     xtype: 'linechart',
     store: store,
     xField: 'date',
     series: [{
      type:'line',
      displayName: 'line1',
      yField: 'val1',
      style: {
       color: 0xFF9999,
       lineSize:2,
       lineAlpha:0.6,
       alpha:0.6
      }
     },{
      type:'line',
      displayName: 'line2',
      yField: 'val2',
      style: {
       color: 0x99FF99,
       lineSize:2,
       lineAlpha:0.6,
       alpha:0.6
      }
     }]
    }
    

    「color」はサンプルのコードにあり、意味も明らかだ
    「lineSize」は線の太さでデフォルトの太めの線は「3」が設定してあるように思える
    「lineAlpha」は線の不透明度でデフォルトは「1(おそらく不透明)」になっている
    「alpha」はマーカの不透明度だと思う

    他に
    images 円グラフのみ
    image 折れ線グラフのマーカのイメージ?
    mode
    colors
    borderColor
    borderAlpha
    fillColor
    fillAlPha
    size
    lineColor 折れ線グラムのみ
    lineAlpha 折れ線グラムのみ
    showAreaFill
    areaFillAlpha
    connectPoints 折れ線グラムのみ
    connectDistinuousPoints
    distinuousDashLength
    showLabels
    hideOverlappingLabels
    font
    visibility 円グラフのみ
    skin 折れ線グラフのみ

    とが、charts.swf > action(3) > Charts > setSeriesStyles() のswitchのcaseラベルにあったが、未調査。

    2011年2月8日火曜日

    ExtJS の Grid の ヘッダーにスタイルを適用する

    ExtJS の Grid の ヘッダーにスタイルを適用にまよったのでメモ

    カラムの指定に
    {header:'field1', css:'color: green;'},
    のように指定すればセルにcssが適用されるが、
    ヘッダに適用する為のコンフィグオプションは無くどうするか迷った。

     http://www.sencha.com/forum/showthread.php?94369-Grid-Header-Style&p=447180
    をみるとidを指定しスタイルを書けばいいらしい。

    スタイルの指定に迷ったが
    {header:'field1', id:'field1'}
    としたならば、
    .x-grid3-hd-field1 {
      color: green;
    }
    

    セルも指定したいなら、
    .x-grid3-hd-field1,.x-grid3-col-field1 {
      color: green;
    }
    
    とかけばいい。