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が入っている状態なら何も必要無いのでそういう点で利用は出来るかもしれない。

処理が継続されるエラー発生時に例外を投げるというもの。エラー処理を例外で統一させる事が出来る。

class IOException extends Exception {}

function errorHandler($errno, $errstr, $errfile, $errline) {
    if (false !== substr('failed to open stream', $errstr)) {
        throw new IOException($errstr, $errno);
    }
    throw new Exception($errstr, $errno);
}

set_error_handler('errorHandler');

try {
    file_put_contents('cosmos:\\1.txt', 'asdf');
} catch (IOException $e) {
    echo 'IO exception: ' . $e->getMessage();
} catch (Exception $e) {
    echo 'Unknown exception: ' . $e->getMessage();
}

PHP coding tip: Convert notices and warnings into Exceptions