ラベル PHP の投稿を表示しています。 すべての投稿を表示
ラベル PHP の投稿を表示しています。 すべての投稿を表示

2008年4月15日火曜日

PHPのLDAP関数でActiveDirectoryに接続 - その3.

 今回は単純にPHPから既存のActiveDirectoryへ接続するのでは無く、ActiveDirectoryの設定(と言っても極一部 orz)とSSL経由での接続を考慮に入れた場合のメモ.
 何故SSL経由かと言うと、ActiveDirectoryのパスワード情報をLDAP通信で変更したい場合、SSL経由の接続じゃないとサーバーに怒られるからです.
 ※変更しないなら、SSL有効時においても平文で通信可能ではあります.

 // 2008-04-18 WEBサーバーにCentOS 5.1を使用した場合のメモを追加.
 今回の主な環境.
  ・ActiveDirectoryサーバー … Windows Server 2003(Std/Ent)
  ・WEBサーバー
    Windows … Windows XP Professional(IIS 5.1)
    Linux  … CentOS 5.1(Apache2.2.3)
  ・PHP
    Windows … Version 5.2.5
    ※php.iniの extension=php_ldap.dll のコメントアウトを解除
    Linux  … Version 5.1.6

 尚、今回WEBサーバー代わりとなったXPは、テスト用ActiveDirectoryには参加していません.

1. ActiveDirectoryのインストール.


 普通にインストールします.
 以上.

 …と思ったけどもう少し書きます.

 「スタート」→「サーバーの役割管理」を選択.
 「サーバーの役割管理」ウィンドウで「役割を追加または削除する」を選択.
 「サーバーの構成ウィザード」ダイアログ「サーバーの役割」で「ドメインコントローラ(Active Directory)」を選択.

 後はお好きな様に設定.
 必要に応じてDNSサーバーも自動的にインストールしてくれます.

 結局書いても書かなくても同じだったか orz

2. SSL証明書をドメインコントローラに設定.


 色々方法はある様ですが、今回は手っ取り早くWindowsサーバーの「証明書サービス」を利用します.
 ※環境により、インストール中にOSのCDを要求されます.
 ※ここでの設定方法は独学であるため、ほぼ100%の確率で誤りがあるかと思われます.

 「スタート」→「コントロールパネル」→「プログラムの追加と削除」を選択.
 「プログラムの追加と削除」ダイアログで、左側の「Windowsコンポーネントの追加と削除(A)」ボタンを選択.
 「Windows コンポーネント ウィザード」ダイアログの一覧から、「証明書サービス」にチェックをつけて「次へ」→「完了」を選択.



 「CAの種類」の指定で、「エンタープライズのルートCA」を選択します.
 「スタンドアロンのルートCA」でも行けるかもしれませんが未確認.
 時間があれば後日入れなおしてみるかもしれません.

 ※面倒になったのでここから画像なしで行きます orz

 「CA識別情報」の指定で、共通名に適当(適切)な名称を指定します.
 ここで指定した名称は、証明書の名称であったり、識別名(ダイアログの下の方)で参照されます.
 「証明書データベース」の指定では構成情報の保存場所を指定しますが、後からここに出力されたセキュリティ証明書(*.crt)を参照する事になるので、分かりやすい場所がいいかと思います※1.

 インストール後、「スタート」→「コントロールパネル」→「管理ツール」→「証明機関」と言う項目が増えます.

 基本的には、この段階でActiveDirectoryのSSL通信がサポートされている…はずです.
 続いて実際に通信の為のセキュリティ設定を行います.

 「スタート」→「コントロールパネル」→「管理ツール」→「既定のドメイン セキュリティの設定」を選択.
 「既定のドメイン セキュリティの設定」ウィンドウの左ペイン(ツリー)から、「セキュリティの設定」→「公開キーのポリシー」→「信頼されたルート証明機関」を右クリックしてプロパティを開きます.

 ここで「登録された Active Directory およびユーザープリンシパル名(UPN)の名前制約条件を満たす証明機関(D)」を選択しないと、SSLを有効にした状態ではPHPからユーザー情報の編集が出来ませんでした.
 …私が他の設定を抜かしているだけかもしれませんけどw

 // 2008-04-18 追記 ↑まったく関係ありませんでした orz

 上記項目選択後、「OK」ボタンを選択してプロパティ画面を終了します.

 再度「信頼されたルート証明機関」を右クリックし、「インポート」を選択します.

 「証明書のインポート ウィザード」で「次へ」→ファイルの指定で、本文『※1』で指定したフォルダにある「****.crt」を指定→「次へ」→「次へ」(選択不可になってるはず)→「完了」を選択して、証明書をインポートします.

 「スタート」→「ファイル名を指定して実行」で
gpupdate /Force
 と入力し、ポリシーを最新の状態に更新します.


【WEBサーバーがLinuxの場合のみ、以下を追加で実行】
 「スタート」→「コントロールパネル」→「管理ツール」→「証明機関」を選択.
 「証明機関」スナップインで、左ペインのツリーから目的のサーバーを探します…と言っても、多分ひとつだけしか無いと思いますけどw
 目的のサーバーを右クリック→「プロパティ」→「全般」タブの「CA証明書」一覧で証明書(多分「証明書 #0」と言う様な名称)が選択されている事を確認して「証明書の表示」ボタン選択.
 更に出てきたダイアログの「詳細」タブを選択→「ファイルにコピー」ボタン選択.
 「証明書のエクスポート ウィザード」で「次へ」→「Base 64 encoded X509(CER)(S)」を選択して「次へ」→保存パスを指定して「次へ」→「完了」ボタンを選択して証明書をエクスポートします※2.
 後でWEBサーバーにエクスポートしたCERファイルを渡す必要があるので、その点を考慮して保存パスを指定した方が良いかと思われます.

※画像が無いので説明が分かり難いかも…英語だけど→「LDAP over SSL - Modifying Active Directory with PHP」の方が分かり易いかもしれません.

 これでActiveDirectoryサーバー側の設定は終了です(本当かどうかは知りませんがw).

3. SSL証明書をWEBサーバーに設定.


 次に、ActiveDirectoryサーバー(本当は証明書サーバーですが)で生成した証明書をWEBサーバー機に設定します.

【Windowsの場合】
 「スタート」→「ファイル名を指定して実行」で
mmc
 と入力し、管理用コンソール(GUIです)を起動します.
 スナップインとかMMCとか言われているアレです.

 コンソールのメニューから「ファイル」→「スナップインの追加と削除」を選択し、「スナップインの追加と削除」ダイアログを表示します.
 上記ダイアログの「追加」ボタン(ダイアログ下部)を選択し、「スタンドアロン スナップインの追加」ダイアログを表示します.
 スナップインの一覧が表示されているので、一覧の下の方にある「証明書」を選択状態にして「追加」ボタンを選択します.

 更にもうひとつ「証明書スナップイン」と言うダイアログが表示されますので、「コンピュータアカウント」を選択し「次へ」.

 管理対象に「ローカルコンピュータ(このコンソールを実行しているコンピュータ)」が選択されている事を確認し、「完了」を選択します.
 「スタンドアロン スナップインの追加」ダイアログの「閉じる」ボタンを選択します.

 「スナップインの追加と削除」ダイアログの空だった一覧に「証明書」が追加されている事を確認して、「OK」ボタンを選択します.

 証明書の分類一覧がツリー表示されたコンソールが表示されるので、「信頼されたルート証明機関」を右クリック→「すべてのタスク」→「インポート」を選択します.
 本文『※1』で出力した「***.crt」ファイルを選択→「次へ」→「証明書をすべて次のストアに配置する」が選択されている事を確認して「次へ」→「完了」を選択し、証明書をインポートします.


 あー長い orz

 この段階で、正常に設定が完了しているか確認するには、Windows Server 2003のCDに入っている「ldp.exe」を使うのが良いかと思います.
 インストーラの保存パスは
  [CD]:\SUPPORT\TOOLS\SUPTOOLS.exe
 です.
 このインストーラでインストール後、
  C:\Program Files\Support Tools\ldp.exe
 を起動します.
 使い方は…面倒なのでヘルプ参照でw

 ちなみに、既にPHPでLDAP接続を試してて「つながんねぇ~!!」という状況の場合は、IISを再起動させる必要があります(コード上で接続プールをクリア出来るのかもしれませんが、私は知りませんので再起動 orz).

 「スタート」→「ファイル名を指定して実行」で「cmd」と入力してEnter.
 出てきたコマンドプロンプトで
C:\> net stop w3svc
C:\> net start w3svc



【Linuxの場合】
 OpenSSLの力を借りてCERをPEMにコンバートし、ldap.confに鍵情報を追加設定します.
 本文『※2』で出力したCERファイルが「export.cer」と言う名称であったと仮定した場合のコマンドは次の通りです.
[??@centos]# openssl x509 -in export.cer -out export.pem
[??@centos]# cp export.pem /path to openldap/cacerts/export.pem
[??@centos]# vi /path to ldap.conf
# 以下、変更箇所
host lightmaterial.example.com
base dc=lightmaterial,dc=example,dc=com
uri ldaps://lightmaterial.example.com
# 以下、追加文言
TLS_CACERT /path to openldap/cacerts/export.pem
TLS_REQCERT never
# 保存
[??@centos]# /etc/init.d/httpd restart



 これでWEBサーバー側の設定は終了.
 後はコードを書くだけだ.

4. PHPからSSL経由でActiveDirectoryに接続してみる.


 ここでは、ActiveDirectoryに以下のユーザー情報が登録されているものとします.
 ・sAMAccountName=t_hokkaido
 ・cn=北海道 太郎 T.H
   sn … 北海道
   givenName … 太郎
   initial … T.H
 ・SELFオブジェクトのアクセス許可
   パスワードのリセット … ON
   パスワードの変更 … ON

 尚、上記のアクセス許可を確認するには「Active Directory ユーザーとコンピュータ」右ペインの何も無い所で右クリック→「表示」→「拡張機能」を選択した状態で、各ユーザーのプロパティを開き、「セキュリティ」タブのユーザー一覧から「SELF」を選択する事で確認出来ます.
 別に「パスワードの変更」だけでも良さそうですが、「パスワードのリセット」も選択しないとパスワード変更出来ませんでした…またバカなミス(文末追記参照)をしてるのかもしれませんが orz

 ActiveDirectoryの情報は次の通りとします.
 ・DN … OU=Sapporo,DC=lightmaterial,DC=example,DC=com

 こっからはさっさと行きます.

    // Windows環境下では、↓の環境設定をしなければ接続時にエラーが出ます.
    putenv('LDAPTLS_REQCERT=never')
        or die("couldn't setup reqcert-environment...");
    
    // LDAPSを用いてドメインを指定しつつ接続.
    // ※ActiveDirectoryがSSL接続時に使用する標準ポートは「636」です.
    $ldapConn = ldap_connect('ldaps://lightmaterial.example.com', 636)
        or die("couldn't connect to LDAP Server...");
    
    // オプション設定.
    // すべて設定しないと、Windows環境では上手くつながらない場合があります.
    ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3)
        or die("couldn't set protocol version...");
    ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0)
        or die("couldn't set referrals...");

    // バインド.
    if ($ldapConn) {
        ldap_bind($ldapConn, 't_hokkaido@lightmaterial.example.com', 'this_is_password')
            or die("ldap_binding get an error.<br>reasons... " . ldap_error($ldapConn) . "<br>");
        echo "ldap_binding is success.<br>";

        // ログインユーザーの情報を検索してみる.
        if (! ($result = ldap_search($ldapConn,
            'OU=Sapporo,DC=lightmaterial,DC=example,DC=com',
            '(sAMAccountName=t_hokkaido)',
            array('cn', 'sn', 'givenName', 'displayName', 'sAMAccountName')))) {
            // ユーザー情報が見つかりません.
            echo "user not found.";
        } else {
            // 今回はパスワードを変更してみる.
            // パスワード文字列を加工してあるのは、渡す値がUnicode文字列である為.
            // ※パスワード文字列は「"」(ダブルクォーテーション)で囲む必要があります.
            $plainPasswd = '"this_is_new_password"';
            $length = strlen($plainPasswd);
            $unicodePasswd = '';
            for ($position=0; $position<$length; $position++)
                $unicodePasswd .= "{$plainPasswd{$position}}\000";

            // ldap_modifyを使って実際に属性値を更新(ldap_mod_replaceでもOK).
            // ちなみに、パスワード変更後も変更前のパスワードで認証出来てしまう場合は、
            // 2回ldap_modifyしてあげるしか対処法は無さそうです.
            // 多分ldap_unbindしてもログオフした事にならないのが原因だと思います.
            ldap_modify($ldapConn,
                ldap_get_dn($ldapConn, ldap_first_entry($ldapConn, $result)),
                array("unicodePwd" => $unicodePasswd));
        }
        // バインド開放(クローズ).
        ldap_unbind($ldapConn);
    }
    echo "fin.";
 これまでの設定に間違いが無く、且つ私がボケていなければLDAPSで接続した上でパスワードを変更出来たはずです.

 長かった…
 またしばらく長い投稿は止めよう orz

 今回は、かなり参照させてもらったサイトがありますので、一応↓に挙げておきます(100%自己メモ).

[参照サイト]
 ・Using Ldp.exe to Find Data in the Active Directory
 ・How To Enable Secure Socket Layer (SSL) Communication over LDAP for Windows 2000 Domain Controllers
 ・LDAP over SSL - Modifying Active Directory with PHP
 ・unicodepwd and ldifde
 ・PHPマニュアル LDAP関数
  ※↑このページの下の方の英語でかかれたnoteに情報がある.


注)この投稿…投稿してはミス発覚の連続で10回くらい修正してます orz
 そろそろ本気で直書き止めるかな…

/**
 * 2008-04-18 追記/修正.
 *  ・WEBサーバーにCentOS追加
 *  ・DNにbloggerと使っていたものをexampleに変更
 *  ・下記問題に対応
 */
 はい、アホ過ぎます orz

 投稿当初、認証ユーザーが「Account Operators」に属している必要がある…と書きましたが、これが何も変更出来なくなる一番の原因だった様です(本文を見直した時に邪魔なので、斜線ではなく該当する文言ごと削除しました).

【現象】
 1. 「Account Operators」に属するユーザーで認証(ldap_bind)
 2. 自身のエントリーを取得
 3. パスワード変更→正常に処理完了
 4. しばらく普通に属性の変更やパスワードを再変更出来る
 5. 何かの拍子に何の属性も変更出来なくなる

【原因】
 何も変更出来なくなった状態のユーザーを改めて確認すると、所属するグループに「Account Operators」が登録されているが、「セキュリティ」タブのグループから「Account Operators」が消えていた.
 更に「SELF」のアクセス許可が「パスワード変更」のみONになっていた(それ以外はすべてOFF).

 つまる所、「Account Operators」権限で自分自身のパスワードをむやみに変更すると、何か変な事になる、と言う事ですね!!
 …全然つまってないじゃん orz

 何でこんな事になるのか分かりませんが、また時間を作って原因追求と言うか解析してみたいと思います.
 本当に無駄な時間過ごしたなぁ…

 あ、直しついでに「WEBサーバーとしてLinux(CentOS)を使った場合」も追記しました.

2007年12月20日木曜日

唖然とする某システム.

 最近、某所にて某システムを触る機会がありました.
 …某某ばかりじゃ分からないけど orz
 その某システムが凄い訳です.

 PHPで書かれているんですが、まず、エラー画面にエラーハンドラで受け取ったエラー内容を「ほぼそのまま」表示します.
 ○○○.phpの○○行目で、○○関数の引数が○○だからエラーが出ましたよ、と(実際のメッセージは異なります).
 そりゃもう親切丁寧に教えてくれる訳ですよ orz

 更にエラーが発生した際、場合によってはクエリを画面に出力してくれます.
 まぁ!なんて親切なんでしょう♪ … orz
 ※これは多分意図した訳ではなく、デバッグ気分で echo ったであろうコードを削除し忘れているかと思われます

 とまぁ、致命的と思われる事はこの辺にしておき、一番衝撃的な事(いや、上のも十分衝撃的なんですが、自分でもやってしまいそうと言う意味での衝撃的)が、Ajax周りの挙動.
 私は余りそんな組み方をしませんが、

 1. 複数の選択欄(SELECT)がある
 2. 選択欄Aを選択するとAjaxが走り、サーバーへ選択欄Bのリストを取りに行く
 3. サーバーから取得したリストを選択欄Bへセットする
 (選択欄Dまで繰り返し...省略...)

 と言う「まぁ、やるっちゃーやるな」と言う処理なんですが(私は選択する度にトラフィックが発生するのが嫌で、余程膨大な選択種が無い限り最初に全部読み込みます)、今回出くわした環境がアナログ回線だったが為に、下の様な挙動を連発する訳です.

 選択欄Aで選択→選択欄Bが空欄のまま→ボーっと待つ→数秒後選択欄Bにリストがセットされる
 選択欄Aで内容1を選択→選択欄Aで内容2を選択しなおす→数秒後選択欄Bには選択欄Aの内容1に連動するリストがセット

 正直使う気が失せますね、これじゃ……
 良くAjaxでデータを読み込む時に、ローディング画像を表示しつつDIVなどで画面を保護する作りにしますが、選択欄を連動させるくらいでそんな保護しませんしねぇ.
 逆にいちいち選択内容変更する度に(選択欄を連動させる為だけに)ローディング画面になったら、Ajaxな意味が無くなるくらい煩わしくなりますし.

 まぁ、平成19年も師走を迎えた昨今、そんなWindows95を思い出す速度環境下なんて余り無いでしょうから、そんなに気にする必要も無いのかもしれませんけどw

 とは言いつつ、今回出くわした様な作りにはしない様に心掛けようと心底思った今日この頃.

/**
 * 追記.
 */
 この投稿書いてから、Gmailが高速化する手段として「メッセージをあらかじめ読み込んでおくため、メールを開くときにGoogleサーバにアクセスしなくてもよくなり、メールの表示が速くなる」(『@IT』より一部抜粋)方法を選んだ事を思い出しました.
 Ajaxも使い方次第では旧来よりユーザビリティを低下させるかもね、と.
 ※Gmailの場合、正確には「Ajaxなバックグラウンド処理で事前に読み込む」と言う方式らしい

2007年12月13日木曜日

MTOS: Movable Type オープンソース・プロジェクト.

 久々の投稿.
 仕事が忙しかった訳では無く、単に家で仕事をしていただけです.

 … orz

 そんなどうでもいい近況報告はおいておき、Movable Typeのオープンソース化プロジェクトが始まった様です.
 まぁ、前からソース確認出来てたみたいですけど、lightmaterialは余り興味が無く、一度も見た事がありませんでした.
 しかし「折角オープンソース化したんだしな」と、この機会にチラッと中身を覗いておこうと思い早速ダウンロード.

 さて、どこから見るかな~
 お!この「mtdb_base.php」って言うファイルは結構サイズがあるし、読み応えがありそうだ!
 どれどれ…


 ……ぎゃーーーー!!!
 き、き、き、汚ねぇよ!!! orz

 なんじゃこりゃ…
 いや、そりゃ読める範疇なんだけどもね.
 それにしたって、コメント全然無いし、トリッキー(初心者が見て理解不能な書き方. lightmaterialは一番嫌いです)だし、インデントがファイルによってタブ文字だったりスペースだったりするし、SQL文がコード中に直書き されてるし……

 有名所の会社でも、あんなコード書いてる所があるんだなぁ…と思い知った今日この頃.
 他の会社もあんなモンなんでしょうかね?
 ま、まさかね……w

/**
 * 追記
 */
 そう言えば、以前サクラエディタと言うソフトのソースを見た時も「色んな人が書いてるから読みにくいなぁ」と思ったものですが、私としてはMovable Typeの方がよっぽど読みにくいです(C++とPHP比較するのもアレですが).
 そんな感じです、はい.

2007年10月23日火曜日

Windows版PHPのLDAP関数.

 一言メモ.

 以前書いたLDAP関数に関しての投稿は、Linux上で動作しているPHPで試していた訳ですが、マニュアルにある通り、Linux版ではOpenLDAPのクライアントがインストールされている必要があります.
 で、私はてっきりWindows版もOpenLDAPのクライアントが入ってないと駄目なんだろうと思っていたんですが、実は無くても全然問題ないんですね.
 これは楽だ!!

 …って、心底今更な話か orz

 マニュアルを見ると、LDAP関数の要件
LDAP サポートを有効にして PHP をコンパイルするには、 » OpenLDAP あるいは » Bind9.net から LDAP クライアント ライブラリを入手し、コンパイルしておく必要があります。

 と書いてあるから、完全に勘違いしてました.

2007年10月2日火曜日

PHPのLDAP関数でActiveDirectoryに接続 - その2.

 メモ走り書き.

 ActiveDirectoryにはuidが存在しない様だ.
 へぇ~…って、じゃあログインユーザー自身の情報をどうやって取得すればいいんだよ!? orz

 普通に考えればldap_searchのフィルタに

 '(uid=' . $userid . ')'

 の様に指定するんだろうけど、uidが存在しないんじゃなぁ…
 と言う事でインターネットの海にどっぷりと沈みこみ、数時間後にようやく発見.
 何でこんなに時間がかかったかは秘密.
 秘密と言うか「cn」や「uid」をAttribute(属性)と呼ぶ事が分からなかったが為に、かなりのタイムロス orz

 で、結局ズバリのページを見つけて歓喜↓

 LDAP Attributes from Active Directory Users and Computers
 ※画面を少し下にスクロールしたら表があります.

 「uid」の代わりに「sAMAccountName」(または「userPrincipalName」)を使えそうです.
 これで心おきなく情報を取り放題ですよ.


$objConnection = ldap_connect('ldap://example.com', 389);
  or die('could not connect Active-Directory.');
ldap_set_option($objConnection, LDAP_OPT_PROTOCOL_VERSION, 3)
  or die('could not set protocol-version...');

if (! $objConnection) die('connection-object is empty.');
if (! @ldap_bind($objConnection, 'userid@example.com', 'password'))
  die("error.<br>reason... " . ldap_error($objConnection) . "<br>");

if ($objResult = @ldap_search($objConnection, 'OU=exampleou,DC=example,DC=com', '(sAMAccountName=userid)', array('cn', 'sAMAccountName', 'userPrincipalName', 'mail'))) {
  $aryResult = ldap_get_entries($objConnection, $objResult);
  print_r($aryResult);
} else {
  echo 'could not get entries.<br>';
}
ldap_unbind($objConnection);
echo 'finish.';
 ※タグべた書きなのでコードが読みにくいと言うか汚いと言うか投げやりなのは気にしない方向で.

PHPのLDAP関数でActiveDirectoryに接続.

 メモ走り書き.

 PHP4の環境からActiveDirectoryでの認証が必要になったのでテスト.

 ・ADサーバー : Windows Server 2003 Standard Edition

 マニュアルに従い、ldap_bindの第二引数にdnとして

  'cn=userid,ou=exampleou,o=excom,c=JP'
  'uid=userid,dc=example,dc=com'

 等々つっこんでみましたが、
Warning: ldap_bind(): Unable to bind to server: Invalid credentials in ....

 とエラーが出るばかりで認証出来ず.
 何でだ?何でだ?と頭を悩ませる事1時間.

 …ん?これって普通に「userid@example.com」形式じゃ駄目?
 と思い至り、早速やってみると……普通に接続しやがりました orz


$objConnection = ldap_connect('ldap://example.com', 389);
  or die('could not connect Active-Directory.');
//-> ↓2007-10-02追記(これもセットした方が良さそうです)
ldap_set_option($objConnection, LDAP_OPT_PROTOCOL_VERSION, 3)
  or die('could not set protocol-version...');

if ($objConnection) {
  if (@ldap_bind($objConnection, 'userid@example.com', 'password')) {
    echo "success.<br>";
  } else {
    echo "error.<br>reason... " . ldap_error($objConnection) . "<br>";
  }
  ldap_unbind($objConnection);
}


 まぁ、あれです.
 アホです.
 生粋のアホです orz
 また今日も時間を無駄にした...

2007年9月28日金曜日

データベースへの追加・更新…どのクラスで実装すべき?

 何を今更…と言う様な話なのは重々承知ですw

 PHP4もライフサイクルが終了しますので、来年を持って会社で開発・使用していたFrameworkも終了し、ZendFrameworkに乗り換え様かと会社のSEさんが言ってました.

 そうだよね.
 と言いますか、ぶっちゃけ乗り換えがかなりの勢いで遅いですw
 しかしそれもPHP4で運用している環境をサポートしなければならなかったが故(ZendFrameworkはPHP5.1.4以降で動作).
 致し方なしと言った所でしょう.

 所で、lightmaterialを含め会社の人はPHP/.Netを使っているんですが、.Netの影響か会社のPHPFrameworkではDBに対するINSERT/UPDATE/DELETEをマネージャで実装しています.
 勿論正確にはDBAdapter(DataBase用のアダプタ)のメソッドを呼び出すのがマネージャな訳です.
 ADO.Netも似た様な構造です.
 fillしたデータの各行に対して変更を加え、それらDBへのコミットはDataAdapterから行いますが、それを呼び出すのは当然マネージャなりデータを操作するクラスだったりします(個別に操作する別の方法もありますが、オブジェクト指向としては醜い方法です…と思いますw).

 が、ZendFrameworkのマニュアルを読むとDataRowクラスに「save」メソッド(追加・更新をDBへ反映させるメソッド)が実装されています.
 要するに

  $objRow = $objTable->createRow();
  $objRow->save();
  //-> 酒飲みながら流し見してたので本当にTableからcreateRowするかは確認してませんが、普通に考えればTableクラスから生成するかと.

 な訳ですよ.
 …いや、確かにそう言う実装方法も色々と見て来ましたので、別に戸惑う事でも何でもないんですが、ふと「あれ?DBへの追加・更新用のメソッドって、どこに実装すべきなんだ?どこに実装するのがエクセレントなんだ?」と言う疑問が湧き上がって訳です.

 実際の所、どちらの方がスマートなんだろう?
 各Rowに対する変更をTableオブジェクトでストアして一気にコミット…大量のデータが変更される環境では、トラフィック効率が非常に魅力的な方法です(リアルタイム性に欠けるんですがw).
 一方、各Rowにsave/deleteメソッドが実装されていると、小回りが効くと言うか、何かしらのインポート処理では無い限り、こちらの方がリアルタイム性+トラフィック効率が良い様な気もします(さっきと書いてる事違うじゃない…と言う事は無く、一度に大量の処理が発生する場合は前者の方がトラフィックが減りますが、1レコードしか更新しない場合には後者のほうがトラフィックは少なくなります).
 ただしこの方法、数万レコードのインポート処理が発生するプログラムの場合、相当処理が遅いんじゃないの? orz

 うーん.
 難しい.
 Javaのデータベース周りって、どっちの方法がスタンダードなんだろ?
 どちらのアプローチも間違いじゃないから悩ましい.
 でもZendFrameworkに乗り換えるのは殆ど決定事項みたいですので、これからは2つのアプローチに慣れる必要がありそうです.

 それにしても、会社のFrameworkってプログラム量が凄い少なくて済む(と言うか、かなりの勢いで全自動)から気に入ってたんですけどねぇ.
 ちょっと残念です.

2007年9月21日金曜日

PDF出力が一対一の直つなぎ環境で出力されなかった問題.

 先日書いた一対一の直つなぎ環境だとPDFが出力されない、と言う問題.
 なんの事は無い、単にサーバー側のDNS設定がおかしかっただけでした orz

 誤設定 → ルータ環境下でのDNS設定のまま
 正設定 → 直つなぎ相手または自分

 …って…んが?
 何でだ?
 そう言うもんなのか?
 DNS設定がおかしいと、HTML/PHPは正常に表示されるが、PDF出力(PHPから生成)の場合は表示されないって?
 そんなバカな話あるのか?

 うーん…と言う事で、やっぱりコードが悪いのかもw

 これは後日要確認!(と書いて調べてない事が山ほどある事は忘却の彼方へ…時間見て遣り残してる確認事項終わらせないとなぁ).

2007年9月9日日曜日

PHPの配列によるメモリ消費.

 久々更新.

 前から思っていましたが、何故PHPの配列はあんなにメモリを消費するんだろ.
 大きいデータの場合、文字列→配列へexplodeなりすると、メモリ消費量が一気に10倍程度まで跳ね上がります orz
 この傾向は当然ながらデータサイズが小さい物より大きいものの方がより顕著です.
 迂闊に大きいデータを配列化しようものなら、貴重なメモリ資源が喰い散らかされてしまいます(終了時には解放されますが、Linuxのメモリ最適化処理なんかの環境下だと、終了後も一見掴みっぱなしに見えるから気持ち悪い…).
 例えばデータベース.
 データベース回りの機能をクラス化すると、大抵SELECTクエリの結果データを配列へ格納する様に作ると思いますが、この時も直接mysql_fetch_xxxxで処理を回すより結構なメモリ消費量となってしまいます.
 更に、データベースから取得した配列データのエンコードを変換して出力したいとした時、利便性から「mb_convert_variables」(配列データ中のエンコードを指定のエンコードに一括変換する関数)を使うケースが多いと思いますが、この「mb_convert_variables」も曲者で、処理後のメモリ消費が処理前の倍近くになってしまいます.
 分かり易く書くと↓こんな感じ.

echo "MEMORY0 : " . number_format(memory_get_usage()) . " byte(初期値)<br>";

$strBuffer = str_repeat("ABCDEFG,", 10000);
echo "MEMORY1 : " . number_format(memory_get_usage()) . " byte(文字列生成)<br>";

$aryBuffer = explode(",", $strBuffer);
echo "MEMORY2 : " . number_format(memory_get_usage()) . " byte(配列生成)<br>";

unset($strBuffer);
echo "MEMORY3 : " . number_format(memory_get_usage()) . " byte(文字列解放)<br>";

mb_convert_variables("SJIS-win", "UTF-8", $aryBuffer);
echo "MEMORY4 : " . number_format(memory_get_usage()) . " byte(mb_convert_variables実行)<br>";

unset($aryBuffer);
echo "MEMORY5 : " . number_format(memory_get_usage()) . " byte(配列解放)<br>";

 ※PHP5.2.1以前の場合、「memory_get_usage」は「--enable-memory-limit」付きでメイクする必要があります.

 結果↓

MEMORY0 : 59,160 byte(初期値)
MEMORY1 : 139,568 byte(文字列生成)
MEMORY2 : 1,045,532 byte(配列生成)
MEMORY3 : 965,600 byte(文字列解放)
MEMORY4 : 1,637,344 byte(mb_convert_variables実行)
MEMORY5 : 91,424 byte(配列解放)


 内部で単純に複製して変換しているからなのかもしれませんが、メモリ消費が変換対象の配列変数を解放するまで掴みっぱなしになるのは如何なものかと…

 まぁそんな事言い出すと、全部変数解放した後の消費量も気になるじゃねーか!って話になりますけどw

 こうして考えると、世にあるPHP用Frameworkがそれぞれどの程度のメモリ消費なのか、見比べてみるのも面白いかもしれません.
 同等の機能を実現出来るFrameworkなら、エコなコードの方がいいに決まってますから.

2007年7月31日火曜日

EnumeratorとIterator.

 …またやってしまった orz
 週末更新が途絶え過ぎです.
 前回反省した様な事を書いておきながら、全然反省してなかった事が白日の下に orz

 と、デジャヴな反省は置いておき.
 最近Javaの勉強も全然進んでいませ…ここでも駄目すぎる事実が orz
 反省しきりです.
 27時間テレビなんて見るもんじゃありませんw

 話が逸れまくりましたので元に戻して.
 Javaの勉強が全然進んでいないと言う駄目すぎる状況にある訳ですが、会社でPHPのコードを書いている時にフと「あれ?…EnumeratorとIteratorって…何が違うんだ?」と言う疑問にブチ当たりました.
 はい、設計の勉強云々以前の問題ですね orz

 いや、これは.Netの弊害です!!
 私も被害者なんです!!! orz

 何でPHP書いててこんな事を考えたのかと言うと、会社では主に「PHP4でも動く事が前提」でコードを書いてますので(もうPHP4系のライフサイクルも終わりますが)、オブジェクト指向ライクな構造をとる場合には、かなりの割合で自力実装が必要です.
 そして、それらは独自Frameworkとして作成・実装している訳ですが、その中でCollectionに対する列挙処理として「Iterator」クラスを作成しています.
 例えば、Collectionクラス(勿論自前実装)には「追加」「クリア」「Item取得」の様な振る舞い+「getIterator」を実装して、列挙処理にIteratorを使用するイメージです.

 ※実際にはこんなにシンプルではありませんw

 んが、.NetではIteratorと言うものは「概念上」ありますが、実際にはEnumerator(IEnumerator)がカバーする仕組みになっていて、lightmaterialの頭の中では「Iterator=Enumerator」と言う図式が完全に出来上がっていました.
 でも、実際にはJavaの実装で見ると、IteratorとEnumeratorには明確な差異があり、IteratorはEnumeratorの後継にあたり、Enumeratorでは実装されていなかった「要素の削除」機能が実装されているものだ、と定義されています.

 あれ?
 何だ?
 ん?

 …結局IteratorとEnumeratorって何が違うんだよぉぉぉぉ~~!!? orz

 Javaなら「はい、そうですか」と明確で明瞭に理解可能ですが、他の言語での実装方法を見ていると、その差異が見えてきません.
 うーん…ここは深く考えずにJavaでは「Enumeratorは過去の産物で、これからはIteratorオンリーです!」と考え、.Netでは「IteratorってEnumeratorの別名でしょ?」と考えておいた方がいいのかなぁ.

 恐るべし列挙子/反復子!
 と言いますか、こんな低レベルな事書いてるPGなんて、全国探しても私くらいのものでしょう orz
 むしろ自分自身が恐ろしい今日この頃.

2007年6月12日火曜日

PHPからのPDF出力でハマる.

 また珍しく平日にメモ.

 PHPからPDF出力すると言えば

  ・PDFLib
  ・FPDF
  ・Zend Framework

 あたりが代表格かと思います.
 Zend FrameworkでのPDF出力はまだ試してませんが(正確には0.xの時に上手く動作させる事が出来ませんでした orz)、PDFLibもFPDFも基本的にはPHPのコードからPDFを吐き出す事に変わりありません.

 そして出力時の問題で有名なのがIEとsessionの問題.
 PHPでsessionを使っている場合、普通にPDF出力するとIEではまったく表示されない状態になります(画面真っ白).
 その為、IEを考慮する場合に於いて、sessionを使ってるコードからPDFを吐き出す時は

  session_cache_limiter('private')

 をsession_start前に宣言してやる必要があります.
 当たり前ですがここまでは知っています.

 んが!
 まさかOpera9でも同じ状況になるとは?! orz
 そんな事まったく知らず「あれ?何で?何で?何で出ないの?」と大ハマり.
 Opera8まではinlineで表示できなかったので、Operaの事はまったく気にしてませんでしたよ.
 まったく、相変わらずOperaは色々と面白い事をやらかしてくれます.
 Opera8→9で、JavaScriptからIFrame内をprintした時の挙動も違いますしw
 今更ですが、Opera9をもう少し触ってみる必要がありそうです.

 そしてもう一個.
 原因は良く分かりませんが、Safariで同じ様にPHP→PDF出力すると、場合によって

  ファイルの最初に%PDF- がありません。

 と言うエラーが出ます(Ver 1.3.2でしか確認してません).
 出ますと言うか出てしまって、またしてもハマりました orz
 未だに原因が分からないんですが、取り敢えず

  header('Content-Length: [バッファサイズ]')

 を送出しなければこのエラーが起きない事が判明.
 なんだかなぁ.
 対処出来てるのはいいんですが、非常に気持ち悪いです.
 どっかでコード間違えてるんだろうか?
 でもSafari以外で出ないんだよなぁ orz

 しかも、今日『Windows版のSafari』が開発中である事が発表されました.
 iPodでiTunesが普及した様に、抱き合わせ戦法でWindowsにSafariが普及した場合、無視できない存在になるかもしれません.
 正直もうブラウザは増えなくていいですから orz

※現状Windows版Safariはまともに動きません

2007年6月3日日曜日

PDTとSubclipse.

 以前も書いたPHP Development Tools.
 実際に使ってみると、結構いい感じです.
 コメントで日本語なんか使うと、色分けや太字処理なんかで問題が出ますが、その辺が気にならなければ快適に使えます.

 ※使う日本語によって、現象が出る出ないがあります.

 ただ、私のやり方が悪いのか、Subclipseが悪いのか、Subversionで管理されたPHPのコードをPDTで編集しようと思うと、思い通りに行きません.
 前に書いた様に、PDTはPHP Projectとして作成したプロジェクト以外では、コードアシスタントもアウトラインも動いてくれませんので、SubclipseでチェックアウトしたプロジェクトをPDT用のプロジェクトにしないといけませんが、この方法が分からない.
 どうやればいいんだろう???

 結局今は力業で.projectファイルをPDT用の物に置き換えてSubclipseとPDTを共存させてますが、まさかこんな力業が正当な使いかたじゃないと思うんですよね.
 どうやったらいいんだろうか?

 うーん分かりません orz

2007年2月18日日曜日

PHP/ApacheをSourceで入れた場合の許可設定.

 メモメモ.
 メモしないとすぐ忘れる.
 前も同じ症状にぶつかって何とか解決したのに、また同じ症状で悩むって…脳味噌年齢70歳かよっ orz

/usr/local/apache2/bin/apachectl stop
httpd: Syntax error on line 53 of /usr/local/apache2/conf/httpd.conf: Cannot load /usr/local/apache2/modules/libphp5.so into server: /usr/local/apache2/modules/libphp5.so: cannot restore segment prot after reloc: Permission denied

 SELinuxで引っかかってるから、chconで権限設定.

[??@fedora]# chcon -c -v -R -u system_u -r object_r -t textrel_shlib_t /usr/local/apache2/modules/libphp5.so

 これでok.