Apelog

Zend_Mailでメールする際のFromヘッダ作成時にメールアドレスのみ渡して名前を渡さない場合

"" <foo@bar.com>

の様な形で生成されてしまう。

この状態でDoCoMo宛にメール送信を行うとDoCoMo端末から返信が出来なかった(確認したのはD905とPROSOLID μ)

ダブルクォーテーションで囲われた所が無ければ返信が出来たのでFromヘッダ生成メソッド部を変更。

public function setFrom($email, $name = '')
{
    if ($this->_from === null) {
        $email = strtr($email,"\r\n\t",'???');
        $this->_from = $email;

        $this->_storeHeader('From', $email, true);
    } else {
        /**
         * Error
         */
    }
    return $this;
}

RailsやCakePHPなどのフレームワークではお馴染みのfindBy系メソッドをZendFrameworkで実装したいということで。

Zend_Db_Tableクラスを継承したMyZend_Db_Tableクラス内のマジックメソッドで実装し、ModelはMyZend_Db_Tableを継承する。

<?php
class MyZend_Db_Table extends Zend_Db_Table {
    /**
     * 論理削除カラム名
     * @var string
     */
    private $_deleteCol = 'delete_time';

    /**
     * findAllBy[ColName]($value [, $flag])でデータアクセス
     *
     * $flag = true で論理削除された行は除外して取得
     *
     * @param string $method
     * @param array  $args
     * @return Zend_Db_Table_Row_Abstract
     */
    public function __call($method, $args) {
        if (preg_match('/^findAllBy(\w+)?$/', $method, $matches)) {
            $field = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $matches[1]));

            $where = array();
            $where["`$field` = ?"] = $args[0];

            if (isset($args[1]) && $args[1] === true
                && in_array($this->_deleteCol, $this->_cols)) {
                $where[] = "`{$this->_deleteCol}` IS NULL";
            }
            return $this->fetchAll($where);
        }
        throw new Zend_Db_Table_Exception("Call undefine method $method()");
    }
}

これで$model->findAllByName(’Design’);のようにアクセスすれば`name` = ‘Design’という条件でのデータアクセスが可能になる。

データは論理削除する事が多いのでfindAllByName(’Design’, true)の形の呼び出しで
`name` = ‘Design’ AND `delete_time` IS NULL

有効な行のみ返すようにしている。

まぁそんな感じで移植実装がおこなわれていくわけです。

Smarty互換でSmartyより軽量なTemplateLiteを使う
に書いた後適度に使っていたところ、Smartyと決定的な差異を発見。

TemplateLiteはオブジェクトにアクセスできない。はじめからオブジェクトをアサインしないような作りにしておけば問題無いものの、既にSmartyでオブジェクトをアサインして使っている場合、テンプレートエンジンをそのまま差し替えるわけにはいかないので注意。

例えば、Zend_Validateのエラー結果を纏めて以下のようなクラスに渡したオブジェクトをアサインしているとする。

<?php
/**
 * @package Sample
 */
class Sample_Validate_Result {
    /**
     * @var array
     */
    private $_errors;

    /**
     * @param array $errors
     */
    public function __construct(array $errors) {
        $this->_errors = $errors;
    }

    /**
     * エラーの有無を返す
     *
     * @param string $name
     * @return mixed
     */
    public function __get($name) {
        if (array_search($name, $this->_errors) !== false) {
            return true;
        } elseif (!array_key_exists($name, $this->_errors)) {
            return false;
        } else {
            return new Sample_Validate_Result($this->_errors[$name]);
        }
    }
}

その状態でテンプレートからアサインしたオブジェクトのメンバ変数にアクセスしようとTemplateLiteの場合、evalに失敗してエラー。

<?php
// Validatorから帰ってくるエラー例
$errorArray = array(
    'nickname'    => array('isEmpty'),
    'mailaddress' => array('tooLong', 'invalid')
);

// コントローラー内でエラーオブジェクトをアサイン
$this->view->errors = new Sample_Validate_Result($errorsArray);

// Smartyならこれができる
{ if $errors->nickname->isEmpty }

完全互換だと思っていて差し替えが後になってしまうと、影響範囲も広がってしまうので記憶の片隅にでも。

ウノウラボ Unoh Labs: PHPで暗号化・復号あれこれ
に暗号化の話が出ていたので、追記する形で他にいくつか。

同じ記事を書いていた前のBlogが消えてしまったので流し書き程度に。

gnupg 関数

PHPからOpenPGPであるgnupg 関数を利用して鍵方式の暗号化がおこなえます。

gnupg関数を使用する為にPECLのgnupgモジュールが必要。

速度的にも以降の方法に比べて早い。PGP暗号化では一番最初に検討したい。

PEAR::Crypt_RSA

PEAR::Crypt_RSAパッケージで公開鍵暗号がおこなえる。

GMP, BigInt, BCMathの3種類が使え、速度は(GMP>BigInt>BCMath)でGMPを基準として(1倍>5倍>250倍)遅い。暗号化するものが少なければ問題無いものの、多くなると速度以外でも色々と問題がある。

動作環境はDebian / PHP5.2.0 / Crypt_RSA 1.2.0bで確認。

  • GMPを使うと暗号化時にメモリリーク(PHP自体)、復号は大丈夫。
  • BigIngを64bit環境で使うと暗号化できない(空文字になる)。

PEAR::Crypt_RSAを使うしか選択肢が無い状態で、3000レコード(1レコード1M)ほどのデータを各行暗号化してDBに入れ、しばらくして復元した時、テスト環境が32bitの本番環境が64bitで泣いた記憶があるので(結局分割してバックグラウンドでGMPで暗号化した)、そもそも環境が違うなんて・・・というのはおいておき、もしかしたら特有の問題かもしれませんが似たような環境で使う人は注意

exec()でgpgコマンドを叩く

http://www.alt-php-faq.org/local/65/

もしかすると幾多もの外的要因によりこういった方法を選択せざるを得ない時もあるかもしれない。GnuPGが必要なのでインストールしておく。

とても重いので頻度が少ないならまだしも、多くなると実用には耐え切れない。既にGnuPGが入っている状態なら何も必要無いのでそういう点で利用は出来るかもしれない。

Ruby on Railsで共同開発をしようということになり、以前RoRのインストールだけはしたものの以降触ってないのでこれを機にRuby・RoRの勉強も兼ねて開発に参加してみる。

http://www.aptana.com

開発環境としてEclipseのRedRailsプラグインを使おうとしていたら、EclipseベースのAptana Studioというものが公開されていた(10/30にversion1.0)ので使ってみる事にする。あと、Aptana StudioのPHPプラグインをちょっと使ってみたかった。

Aptana Studioインストール

公式サイトからコミュニティ エディションをダウンロード。プロフェッショナルエディションはJSONエディタ、IEデバッグ機能、リモートプロジェクト作成機能、FTPS/SFTPサポート、レポートエンジン機能が使えて、優先サポートと最新開発版へのアクセスが可能らしい。

Aptana StudioはEclipse3.2ベースらしいのでPleiadesで日本語化する。ファイル名がEclipseとは違うのでPleiadesの設定はAptana.iniに書き、Aptana.exeを-cleanオプションで起動する。

Aptana.iniの最終行にdefault.splashを付けないと起動ロゴが変わる。

-name
Aptana
-vmargs
-Xmx384M
-Xms128M
-Djava.awt.headless=true
-XX:MaxPermSize=128m
-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar=default.splash

起動後、[ヘルプ]→[Aptana開始ページ]からPHPとRuby on Railsをインストール。RedRails(0.9.1)も問題無く日本語化されている。RedRailsはMylyn用のプラグインもあったりするので必要なら入れておくこと。

SVN用にSubversiveと、半角スペースが見えないエディタは嫌なのでJStyleもインストール。

設定

[ウィンドウ]→[設定]→[Rails]→[構成]でRailsとRakeのパス指定する。mongrel_railsを使いたい場合はここでパスを指定する、使わなければ空欄。

svn checkoutするなり、新しいRailsプロジェクトを作るなりで「RedRailsパースペクティブ」を開く。デフォルトで下部にあるサーバータブの追加ボタン(最小化ボタンの左)を押しプロジェクトで使用するサーバー・ポート・環境を指定。

この状態でサーバーの開始・停止、ジェネレーター、Rake、コンソール(script/console)がAptana Studioから使える。

http://image.blog.livedoor.jp/apecell/imgs/c/7/c73b18a5.png

とりあえずこんな感じで開発スタート。rails.vimも使ってみたいけど、とりあえずこれで。まずはRubyの書き方に慣れないと開発効率以前の問題かな。

PHP用のプラグインは、定義ジャンプも無いし補完も変数レベル(メソッドは無理)なので使う理由は今の所無さそう。