PEARライブラリを利用する際の注意点

プロジェクトのコードを眺めていて気になる点があったので社内で周知すると共に、せっかくなのでブログにも書いてみるテスト。

PEAR DBでgetする際の戻り値

今さらPEAR DBを使うこともそうそうないと思うが、保守案件やレガシーなシステムへ追加で載せる場合などではまだまだ使われてるっつーことで。
それにPEAR DB以外でも気をつけなければいけないことには変わりないし。


前置きが長くなった。

PEAR DBでSELECTの結果が0件の場合の戻り値はメソッドによって違うので気をつけよう。

メソッド 戻り値
getOne() NULL
getRow() NULL
getCol() NULL
getAll() array() ※空配列
getAssoc() シラネ
query() DB_Result


getOne()はまぁいいとして、getRow()やgetCol()の場合に配列を期待したコードを書くとバグの元になるので注意が必要。

<?php
$row = $db->getRow(...);    // 0件の場合$rowはNULLになる
foreach ($row as $key => $value) {  // ここでWarningが出る
    ...
}
?>

逆にgetAll()でNULLチェックしてハマるということもあるかもしれない。

PEAR::isError()とDB::isError()

これもPEAR DBに限らないんだけど、ライブラリで独自にisError()が定義されている場合、そのライブラリ用のエラーオブジェクト(DB_Errorとか)が用意されていたりして、isError()は自ライブラリに関わるエラーかどうかしか判定していない場合があるので注意。
たとえばPEAR DBの場合、PEAR/DB.phpで以下のように定義されている。

<?php
/**
 * Determines if a variable is a DB_Error object
 *
 * @param mixed $value  the variable to check
 *
 * @return bool  whether $value is DB_Error object
 */
function isError($value)
{
    return is_a($value, 'DB_Error');
}
?>

この場合、他のPEAR_Errorオブジェクトには引っかからないので、「エラーはPEAR::raiseError()で返しましょう」なんていうルールに則ってコーディングしている場合は誤判定に注意。

<?php
$item = getItem(1, 1);
if (DB::isError($item)) {
    echo $item->getMessage();
    return false;
} else {
    displayItem($item);
}

/**
 * 指定のカテゴリのアイテム一覧を取得する
 *
 * @access public
 * @param int $category_id カテゴリID
 * @param int $item_id アイテムID
 * @return mixed アイテム情報の連想配列が返る
 *               エラーの場合はPEAR_Errorが返る
 */
function getItem($category_id, $item_id) {
    // 省略

    $row = $db->getRow(...);
    if (DB::isError($row)) {
        return $row;
    } else if (!isset($row)) {
        return PEAR::raiseError('data not found!', ERROR_ITEM_NOT_FOUND);
    }

    return $row;
}
?>

上記のようなコードを書くと、getItem()が'data not found!'を返した場合、displayItem()が呼ばれてしまう。
この場合PEAR::isError()で判定しておけば、とりあえずPEAR::raiseError()のエラーは全て捕捉できる。
DB_Errorしか返ってこないことが分かっているのならいいが、他のエラーオブジェクトが返ってくる可能性を考慮するとPEAR::isError()で判定しておくのが無難。
もちろん必要があるなら戻り値に合わせて適切な分岐をしたようがよいが、とにかくエラーをスルーしてしまうのはまずいので気をつけよう。

<?php
if (PEAR::isError($item)) {
    echo $item->getMessage();
    return false;
}
?>