« 2009年9月 | トップページ | 2009年11月 »

2009年10月

Googleカレンダーに関するまとめ

twitterで書いたのをそのまま持ち込み。


○Googleカレンダーに関するドキュメント
メモ: Google カレンダーの API とツール - Google Code - http://code.google.com/intl/ja/apis/calendar/
メモ GoogleカレンダーのAPIについて書いてあるサイトを教えてください。 特にPHPとの連携があるとうれしいです。個人のブログや掲示板のトピックなどでも結構です。 具体的に.. - 人力検索はてな - http://q.hatena.ne.jp/1182325546
メモ(+見てる) Data API Developer's Guide: The Protocol - Google Calendar APIs and Tools - Google Code: http://bit.ly/44JTzz
見てるぅ> AuthSub for Web Applications - Accounts APIs - Google Code - http://code.google.com/intl/ja/apis/accounts/docs/AuthSub.html
メモ: Googleカレンダーの要素定義はここにある。>Common Elements: "Kinds" - Google Data Protocol - Google Code - http://bit.ly/yNAKW

このへんを参考にすればいいよというまとめ。
認証はAuthSubを使います。

|

ereg使っちゃいけません(ダメダメ♪ダメーッ)

まぁ、いままでereg多用しとったわけです。
ereg関数はPHP6.0でコアから消える予定【PHP】 - Programming Magic
そんなわけでereg厨の俺涙目状態、なわけです。

というか、実はeregにはさらにヤバい問題がありまして。
23. 関数とバイナリセーフ - PHP TIPS:ITpro
こちらの説明がわかりやすいので読んでいただければ、どういうことかは理解できると思います。
Cから入ってる人はひっかかりやすい部分だと思います。

とりあえず、実際にコードかいてみました。

<?php
header( "Content-type: text/html ; charset=utf-8" ) ;
?>
<HTML>
<TITLE>TEST</TITLE>
<BODY>
<P>テスト。
<P>\0 を途中に入れるとどうなる?
<P>\0 あり
<?php
$u = "1234567890" . "\x00" . "HOGEHOGE" ;
print "<P>PRINT raw = " . $u . "<br />\n" ;
print "htmlspecialchars = " . htmlspecialchars( $u ) . "<br />\n" ;
print "strlen = " . strlen( $u ) . "<br />\n" ;
print "is_numeric = " . var_export( is_numeric( $u ) , TRUE ). "<br />\n" ;
print "ctype_digit = " . var_export( ctype_digit( $u ) , TRUE ) . "<br />\n" ;
print "ereg(\"[^0-9]+\") = " . var_export( ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "mb_ereg(\"[^0-9]+\")=" . var_export( mb_ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "preg_match(\"/[^0-9]+/\")=" . var_export( preg_match( "/[^0-9]+/" , $u ) , TRUE ) . "<br />\n" ;
?>
<P>\0 なし
$u = "1234567890HOGEHOGE";
print "<P>PRINT raw = " . $u . "<br />\n" ;
print "htmlspecialchars = " . htmlspecialchars( $u ) . "<br />\n" ;
print "strlen = " . strlen( $u ) . "<br />\n" ;
print "is_numeric = " . var_export( is_numeric( $u ) , TRUE ). "<br />\n" ;
print "ctype_digit = " . var_export( ctype_digit( $u ) , TRUE ) . "<br />\n" ;
print "ereg(\"[^0-9]+\") = " . var_export( ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "mb_ereg(\"[^0-9]+\")=" . var_export( mb_ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "preg_match(\"/[^0-9]+/\")=" . var_export( preg_match( "/[^0-9]+/" , $u ) , TRUE ) . "<br />\n" ;
?>
<P>\0 なし、数字のみ
$u = "1234567890";
print "<P>PRINT raw = " . $u . "<br />\n" ;
print "htmlspecialchars = " . htmlspecialchars( $u ) . "<br />\n" ;
print "strlen = " . strlen( $u ) . "<br />\n" ;
print "is_numeric = " . var_export( is_numeric( $u ) , TRUE ). "<br />\n" ;
print "ctype_digit = " . var_export( ctype_digit( $u ) , TRUE ) . "<br />\n" ;
print "ereg(\"[^0-9]+\") = " . var_export( ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "mb_ereg(\"[^0-9]+\")=" . var_export( mb_ereg( "[^0-9]+" , $u ) , TRUE ) . "<br />\n" ;
print "preg_match(\"/[^0-9]+/\")=" . var_export( preg_match( "/[^0-9]+/" , $u ) , TRUE ) . "<br />\n" ;
?>
</BODY>
</HTML>

¥x00(¥0)の文字を含むものを間に入れてみると、入れてないとで、動作が変わります。

テスト。

\0 を途中に入れるとどうなる?

\0 あり
PRINT raw = 1234567890HOGEHOGE
htmlspecialchars = 1234567890HOGEHOGE
strlen = 19
is_numeric = false
ctype_digit = false
ereg("[^0-9]+") = false
mb_ereg("[^0-9]+")=1
preg_match("/[^0-9]+/")=1

\0 なし
PRINT raw = 1234567890HOGEHOGE
htmlspecialchars = 1234567890HOGEHOGE
strlen = 18
is_numeric = false
ctype_digit = false
ereg("[^0-9]+") = 1
mb_ereg("[^0-9]+")=1
preg_match("/[^0-9]+/")=1

\0 なし、数字のみ
PRINT raw = 1234567890
htmlspecialchars = 1234567890
strlen = 10
is_numeric = true
ctype_digit = true
ereg("[^0-9]+") = false
mb_ereg("[^0-9]+")=false
preg_match("/[^0-9]+/")=0


本来は「¥0あり」「¥0なし」で同じ値を返さないとなりませんが、eregだけ違う値が返ってきてますね。
たとえばrequest.phpで引数pにid(INT)を渡すとします。こういうコード書きますよね。
<?php
$u = $_GET['p'] ;
if ( ereg( "[^0-9]" , $u ) ) ) {
// error !
} else {
// ok ! go !
$query = "SELECT * FROM table01 WHERE id=$u" ;
mysql_query( $query , $connection ) ;
(以下略)

で、リクエストを投げるときにこんなものを投げます。
http://example.com/request.php?p=12345678%00%20union%20(以下略)

すると、$u には、 "1234567890¥0union (以下略)" という文字列が渡ります。
ereg( "[^0-9]+" , $u ) は(さっきの結果をごらんのとおり) FALSE がかえりますので、SQL文を作るところに飛んできます。
$queryで展開されるのは、"SELECT * FROM table01 WHERE id=1234567890¥0 union (以下略)" ・・・。
はい・・・オワタ\(^o^)/、ですね。
そういうことです。

ちなみにこの系列で同様の影響を受けるのが以下の関数。これらはPHP6.0でereg()同様に廃止となります。
・ereg_replace()
・eregi()
・eregi_replace()
・split()
・spliti()
ちなみに正規表現を使わない場合、split()ではなくexplode()を使うといいです(そのぶん高速、かつバイナリセーフ)

この場面の・・・いや、今後の対策としては、
・mb_ereg() にする (いちばん簡単かつ確実)
・preg_match() をはじめとした PCRE関数に転向 書き換える( /〜/ を入れる必要があるので面倒)
・数字なら、is_numeric() とか ctype_digit() とかを使うようにする
あたりでしょうか。

参考:PHPネタ関連メモとか

|

見やすいPHPコードの書き方(但し我流)

あちらのほうで書いたとおり、昨日参加してきました、わんくま東京勉強会#38
そこでのライトニングトークの2本目で話した内容についてです。

ちなみに当日使ったスライド(PDF)
まぁ、ソースがわりとひどかったりします(苦笑)。
設定読むのにglobal使ってたりとか、SQLを複数行に分けてかくときの処理が割とひどいとか・・・ねー。
実は当日会場で作ったスライドで、5分という限られた時間のなかでおさえておきたいところだけを言ったんで、細かいところがいろいろとおかしかったりもしますが、そこんとこはご容赦をー。

最近はPHPでもいいフレームワーク(CakePHPとかsymfonyとか)があるのでそちらに頼ればいいんですが、頼らなくても十分なケースは多々あったりするもので、そのときに注意したいことをいろいろと話してみました。

要約するとこう。
(1)入力から処理されるまでのロジック(コード)と、表示ロジック(HTML)は分けよう
 コードは前半、表示は後半にはっきりわける。
(2)コード処理では以下の順番で書く
 ・共通で読み込む処理
 ・GET/POST/COOKIEからの取得、ならびに値のvalidation check。
  あらかじめ、ここで使用する全部の引数に対して、まとめて行っておく。
  そのときそのときでやると、ミスする可能性が大きいからです。
  ※validation checkしてから、実際に使用する変数に代入するとなおよし
  (私はこの方法でやってます。チェックずみかどうかを意識したいので)
   ↑これ、言い忘れてましたw
 ・入出力データの処理
  GET/POST/COOKIEのデータ、ファイル、データベース・・・
(3)表示部分は表示処理(とそれに必要かつ表示にしか使用されない処理)に徹する。
 ・HTMLタグに使われる文字のエスケープは必須(一部例外を除く)
  ・ちなみにエスケープ関数は自前で書いてます。
   以前htmlspecialcharsを使ったら動作がキモかったので・・・。
(4)データ入出力で特にデータベースを使う場合は、別に関数、出来ればクラスを定義してそこで集中して処理させる
 ・・・はい、メイン部分でSQLを意識しちゃダメです(笑)。
 個人的にはクラス作ってそこで処理させるのが非常に望ましいと思ってます。データの代入・取得とDBへの登録/DBからの取得を1つのインスタンスで出来ちゃうので。
 ただ、PHPのクラスは「あとづけ」的かんじがして結構醜い(笑)部分がありますが、そこさえ我慢すれば十分使えると思います。
 SQLの特別文字のエスケープはこのクラス/関数内で。SQLを生成する段階でエスケープさせます。
(5)変数名のつけかたに(極力)ルールを持たせる
 ・私自身の場合、実体データ、配列、表示用データ、クラスのインスタンス、クラス内変数、・・・・といろいろありますが、先頭1〜3文字でわかるようにしています。

話した内容+αは、ここまでー。


あと言いわすれていましたが、これは言っておきたいんで。
・入ってくるデータは信用してはいけない。
 特にid等に関しては「○○は弾く」ではなく「○○しか認めない」方式にする。
・「magic_quotes_gpc」はオフにすべき。
 正直、入力チェックを徹底している場合、「¥」を勝手に挿入される→取り除くための処理で、かえって邪魔で余計なバグを増やすだけなので(笑)。

今回の件で読んでおいてほしいページとか
@IT:クロスサイトスクリプティング対策の基本
今夜分かるSQLインジェクション対策 - @IT
クッキーに隠されたSQLインジェクション、対策は? - @IT
または、このへんに関連した各サイト。たまに読み返すといいと思います。


参考になりそうなサイト。というかクラスの話が出たので、
@IT:オブジェクト指向の世界(10)
 このへんは、技術というよりは哲学の話ですねー。

|

今日のお題:「祝日判定」

はじめに断っておきますが、万年カレンダーを作る、ってはなしではありません。

お仕事で祝日判定のルーチンを書くことになりまして。
国民の祝日について
…こんなふうに書きました。

function isholiday( $f_date ) {
    // 毎年 mmdd が変わらない日
    //                     元旦   建国   昭和   憲法   みど   こど 
    $fday_common = array( "0101","0211","0429","0503","0504","0505", 
    //                     文化   勤労   天皇   
                          "1103","1123","1223");
    // mmdd が変わる日+国民の休日
    $fday_alter  = array(
        // 2009               成人   春分   振替   海     敬老   国休 
            "2009" => array( "0112","0320","0506","0720","0921","0922",
        //                    秋分   体育
                             "0923","1012") ,   
        // 2010               成人   春分   振替   海     敬老   秋分 
            "2010" => array( "0111","0321","0322","0719","0920","0923",
        //                    体育
                            "1011") ,
        // 2011               成人   春分   海     敬老   秋分   体育
            "2011" => array( "0110","0322","0718","0919","0923","1010")
    ) ;
    $yy = substr( $f_date , 0 , 4 ) ;
    $mm = substr( $f_date , 5 , 2 ) ;
    $dd = substr( $f_date , 8 , 2 ) ;
    $mmdd = $mm . $dd ;
    if ( in_array( $mmdd , $fday_common       ) ) return TRUE ;
    if ( in_array( $mmdd , $fday_alter{ $yy } ) ) return TRUE ;
    // 以下省略

まぁ、はっきり言って

  • なんで(振替休日込みで)全部配列に突っ込んでるんだよー
  • 7月の第3週って設定くらいプログラムで書けよー
  • 春分日・秋分日くらい計算できるだろー

とかいろいろツッコミどころが多いと思います(苦笑)

まぁ、私としては、

  • 毎年1回更新するものだから、そのとき直すだけでいーじゃん
  • 休みはプログラムじゃなくて法律(人間の手)で決まるからどうせまた変わるしー

とかいうヌルい考えで組んだわけです(笑)

これだけじゃ、あんまりなんで、ちょっと遊んでみたいと思います。
twitterでこんなこと書いてみました。

そんなわけで、たとえば「1月の第2月曜日は何日になるかを求めるプログラムを書きなさい」とかいう課題は面白そうだとふと思った。

ぱっと思いついたのは、

1月の1日から数えて何回月曜日がくるかを数えて、2回目だったら祝日

という方法。
●年●月●日が何曜日かというのを求めるには、一番有名な方法としては、Zellerの公式、というのがありますが、今回は手抜きしてmktimeとdateでやっちゃいます。

$yy = 2009 ; // 年
$mm = 1 ;   // 月
$wnum = 2 ; // 第何週?
$wday=date("w",mktime(0,0,0,$mm,1,$yy));
$target_day = ( $wday > 1? $wnum : $wnum-1 ) * 7 + 2 -$wday ;

こっちのほうがわかりやすいかな。

$target_day = $wnum * 7 + 2 - ( $wday + ( $wday > 1 ? 0 : 7 ) ) ;

$wday>1 なのは、日曜が0、月曜が1、火曜が2、となっているためです。
日曜始まりの暦を見ると、月曜日は2番目に位置しています。だから +2 。
第何週なので、そのぶん*7します。
そこから、$wday (1日の曜日) をひきます。曜日が土曜日に向かうほど、月曜日が近くなりますから。

ざっとですが、コードと解説、かいてみました。

|

« 2009年9月 | トップページ | 2009年11月 »