クラウド

なんとかリリースした。

みんなのシフト表

今の自分にはこれで精一杯です。

Amazon EC2(WebサーバーとDBサーバーのホスティング)、Google Apps(メールサーバーのホスティング)、Value Domain(DNS)で構成した。SSL証明書は、ルートがGeoTrustに変わって携帯対応したRapidSSLをApache+OpenSSLに入れた。MySQL, PHP, Ajax(jQuery)でコードを書いてシステムに仕立て上げた。トランザクションデータが20万件でも動作するところまではチューニングした。EC2は当然Reserved Instance契約で動作させている。ランニングコスト自体はPCの電気代よりも安い。監視は当分自分でやるしかないだろう。

IE, FireFox, Chromeで大丈夫なtableタグがOperaだと崩れる

tableタグを使用して作ったページが、IE, FireFox, Chromeでは問題なく表示されていたのに、Operaだけ崩れて表示されるという現象に遭遇した。

色々調べていると、下記のページで原因がわかった。

itochan – Operaで崩れるテーブル(たいした話じゃありません)

つまり、tdタグでrowspan=0としたときの挙動がOperaの場合「ここから全行」と解釈されるのに対し、その他のブラウザは「1行」と解釈されるのが原因だった。驚く事に、Operaの解釈が正しく、他が皆標準とは違う解釈ということだ。

http://www.zspc.com/html40/

より

「・rowspan = NUMBER (CN)

このセルから、指定された数の下方向のセルを連結して1つのセルにします。デフォルト値は1です。0が指定された場合には、このセルから同じセクション(THEAD, TBODY, TFOOT)内の一番下までのセルを連結します。」

abstractなstaticメソッドを書く事に意味はあるのか

phpでクラスライブラリを書いていてふと思った。

DBのテーブルに紐付いたクラスというものを書いていて、それらは共通の基底クラスを持っている。基底クラスはコンストラクタでabstractなメソッドをコールする部分があり、それによって派生クラスで定義されたDBのテーブル定義を取得している。基底クラスは当然abstractだ。

外部からテーブルの定義だけ取得したいなと思ったときにいちいち派生クラスのインスタンスを生成するのは面倒だと思って、abstractなテーブル定義メソッドをstatic宣言してみたが、そうすると基底クラスのコンストラクタがこけてしまった。

Fatal error: Cannot call abstract method kscObjectList::getTableDef() in /home/…../ksc_……class.php on line 50

基底クラスのコンストラクタは、定義された派生クラス側のメソッドをコールしようとせずにabstractなままの基底クラス側のメソッドをコールしようとしてこけている。それにロード時に文法エラーでこけてるのではなく、インスタンスの生成時に実行時エラーでこけている。

ちょっと待てよ?abstractメソッドは派生クラスで定義する事を義務付けるために存在するはずだ。定義しないとどうなる?インスタンスを生成できなくなる。インスタンスを生成できなくする事によって、定義を強制しているというのが自分のもっている感覚だ。

ではstaticなabstractメソッドが基底クラスにあったとする。派生クラス側で定義しないと、派生クラスのインスタンス生成時にエラーになり、派生クラス側で定義すると、インスタンスが生成できるとする。でもstaticなメソッドから、生成されたインスタンスは(パラメータとして外部から渡されたりしない限り)見えないから、staticメソッドの定義強制をインスタンスの生成許可/禁止によって制御するのはよく考えれば非常におかしな話だ。

でも、ここのページにあるように、Factoryメソッドの定義を派生クラスに強制させるために、定義しないとエラーになるstaticメソッドを基底クラスで宣言するというのは意味があるだろう。

そもそもabstractというキーワードは(私が思うに)インスタンス生成時に実行時エラーになるというニュアンスで通っている気がするので、コンパイル時(phpではロード時?)にエラーになるstaticな抽象メソッドのキーワードがあればいいのになあ、と思った。実際には、現時点ではabstractなstaticメソッドは書けません。5.3.0以降に搭載されるLate Static Bindingにしても、基底クラスから派生クラスのstaticメソッドを呼べるけど基底クラス側を抽象化する機能はないらしい。

Release8 build201

私的メモ

Release8 build201
・データとソースを全部utf-8に移行
・DBアクセスをPDOベースに移行し、全クエリーでプリペアドステートメントを実行
・Listとその派生クラスはIteratorを実装し、foreachでまわすようにした
・CategoryクラスはRecursiveIteratorで実装した
・$_GETと$_POSTを安全に取得するためのフィルターをpage基底クラスに作り、必ずフィルターを通すようにした
・jQueryでカテゴリーのUIを改善

次の目標
・アイテム以外のテーブルについてもカラムの追加を仮想化
・仮想カラムに住所型、メールアドレス型、電話番号型、コード型、lookup型を定義
・マスターテーブルの仮想化
・ページクラスのインスタンスを作成するディスパッチャを作成し、そこを通すようにする

Force.com

Force.comはSaleforce.com上にカスタムアプリケーションを作成できるというプラットフォームサービスで、さらに作成したアプリケーションをSalesforce.comユーザーに配布できるAppExchangeというサービスとも連動している。いろいろいじってだいぶわかってきた。

オブジェクト(RDBで言うテーブル)を作成し、Visualforceページ(カスタムWebページ)を作成し、その二つをApexクラス(ほとんどJavaと同じ文法でクラスを定義する)で接着するというのが基本的な流れになっている。Visualforceページには必ず一つコントローラーというApexクラスを定義することになっていて、ページからメンバ変数をいじったりメソッドをコールする事でDBを書き換える。文法やライブラリでガチガチにSalesforceスタイルにさせられるので最初の敷居が高いが慣れれば生産性は高そうだ。ページのテキストボックスやボタンを直接クラスのメンバ変数やメソッドにバインドするような書き方ができる。

ちょうどMicrosoft Accessでテーブルを作成し、ウィザードでフォームを作成するような感覚だ。テーブルとフォームは直接接着されている感覚だが、Visualforceページとオブジェクトはその間に接着剤としてクラスを定義する。その分手間がかかるがそこにロジックを追加していく余地がある。

面白いのは複数ページで同じコントローラーを指定するとメンバ変数でデータの受け渡しができる事だ。セッションにクラスのインスタンスが入っているようなイメージ。

「オブジェクト」と「クラス」がまったく関連のない別のものを指すというのはプログラマ的には「?」だが一般の英語で「オブジェクト=物」だからDBを指すのは自然なのだろう。日本語版でもそのまま「オブジェクト」「クラス」となっている。

Developerアカウントでは2ユーザーでしかテストできないので、ISV and Test Editionの申請をUSのSalesforceにした(日本版にはない)。こっちは10,000回APIコールしかできない制限があるが、20ユーザーでテストできる。

今日はInternal Server Errorが2回出た。いまいち安定していないのはUSクオリティと言うべきだろう。

Couldn’t fetch mysqli

私的メモ

あるオブジェクトをserializeしてunserializeすると「Couldn’t fetch mysqli」が発生した。

そのオブジェクトはあるテーブルに対するラッパーとして作成したもので、ページ内でこのように使われている。

・ページ1:インスタンス化して、serializeした後でセッション変数に格納
・ページ2:セッション変数から取り出して、unserializeし、使用

このクラスは基底クラスのコンストラクタでDBへの接続をおこない、メソッドではそのDB接続を使って読み書きをおこなう。エラーはページ2で発生する。

ラッパーオブジェクトのコンストラクタで作成されたmysqliオブジェクトはserialize->unserializeのなかでその接続を失うようだ(mysqliオブジェクト自体がSerializableではない模様)。そのためラッパーオブジェクトの基底クラスに__wakeup()を追加してunserialize時に再度mysqliオブジェクトを作成するようにした。

よーく考えたらそりゃそうだろうという話だ。ページをまたがる過程でDB接続を保持したスレッドが死んでいるんだから。

unserializeされたオブジェクトのメンバ変数にアクセスするとphpファイルが頭から再実行される

私的メモ

ブラウザーを立ち上げなおしてから一回目の実行時にのみ、以下の現象が再現される

・phpページ1でユーザー定義のオブジェクトをserializeする
・phpページ2で、
          ・それをunserializeする
          ・unserializeされたオブジェクトのメソッドをコールして一部のメンバ変数が変更される
          ・オブジェクトのメンバ変数を参照しようとすると、phpページ2が頭から再実行される
          ・phpページ2は、以上の挙動をするまでの間、html出力はおこなっていない

バグなのか仕様なのかわからないが、html出力をおこなっていなければphpページ2は頭から再実行しても問題ないだろうとphp側が思っている節があるので、以下の修正にてとりあえず回避。

・phpページ2において、unserializeされたオブジェクトのメンバ変数を参照する前にダミー的なhtml出力をおこなう

phpのバージョンは5.1.6。

追記

htmlの出力をおこなってもページの再実行がおこなわれるケースあり。session_startが含まれる関数のコードの宣言をクラスの宣言が行われるコードの後に置くと、今のところ再現せず。前も今もsession_startの実行自体はクラス宣言の後なのに、不可解。

MySQL5

メモ

DBサーバーをMySQL5にアップデートした。引っかかった点

・森けい二の「けい」の字がphpMyAdminからエクスポートした時に文字化けを起こしていたので、インポート時にSQL文法エラーを起こす原因になっていた。「けい」の字はJIS X 0213の字なので取り扱い要注意。
・PHP側からの接続先コンピュータ名が「localhost」では通らなくなっていた。
・接続に失敗した場合の処理が不親切なのでリリースまでに改良する必要あり
・接続に失敗した場合、DBを選択できなかった場合、DBが選択できてもスキーマがなかった場合に分けて処理を変える必要あり。