Zend_ResutのResultオブジェクト(Zend_Rest_Client_Result)について

Zend_RestでYahoo! オークションやショッピングのapiを使用しました。

<?php

define("URL", 'http://auctions.yahooapis.jp/AuctionWebService/V1/Search');
define("APIKEY", '...');

// パラメータ
$param = array(
    'appid'    => APIKEY,
    'query'    => 'nike',
    'type'     => 'all',
    'category' => 0,
    'page'     => 1,
    'sort'     => 'end',
    'order'    => 'a',
);

// クライアントオブジェクト
$client = new Zend_Rest_Client(URL);

// パラメタを設定
foreach($param as $k=>$v) {
    $client->{$k}($v);
}

// リクエスト
$res = $client->get();

if($res->isError()) {
    // エラー処理
}

// xmlの処理
...

必要なパラメータを設定したと思っていたのですが、isError()で毎回falseがでてしまいました。

上記、$resはZend_Rest_Client_Resultオブジェクトです。ダンプしてレスポンスの内容を見ると、商品の情報は正しく取れていました。

Zend_Rest_Client_ResultのisError()のしくみ

Zend_Rest_Client_ResultのisError()の実装を見ると、getStatus()という関数で、リクエストが成功したかどうか判断しています(isSuccess()という関数もありますが、処理は同様です)。

public function getStatus()
{
    $status = $this->_sxml->xpath('//status/text()');

    $status = strtolower($status[0]);

    if (ctype_alpha($status) && $status == 'success') {
    return true;
    } elseif (ctype_alpha($status) && $status != 'success') {
        return false;
    } else {
        return (bool) $status;
    }
}

public function isError()
{
    $status = $this->getStatus();
    if ($status) {
        return false;
    } else {
        return true;
    }
}

public function isSuccess()
{
    $status = $this->getStatus();
    if ($status) {
        return true;
    } else {
        return false;
    }
}

getStatus()では、XPathというノードの値をとって、successか数値で0以外であれば成功、それ以外は失敗の結果を返す処理になっていました。

Yahoo!オークションや、ショッピングapiのレスポンスでは、リクエストに成功しようが失敗しようが、レスポンスにというノードは追加されません。失敗時には、エラーメッセージが含まれる、というノードが設定されたレスポンスが返ってきます。

また、レスポンスコードが400や、500で帰ってくるので、これでチェックすることもできます。

楽天の商品検索apiの場合は、レスポンスにというノードがあり、リクエストの結果を確認できます。

ただ、先頭が大文字のため、やはりisError()は常にtrueになってしまいます。
レスポンスコードは常に200を返すので、yahooのようにここでは判断できません。

結果的にレスポンスの部分は、各apiごとにまちまちでした。
ApiごとにZend_Rest_Client_Resultを継承して、getStatus()をオーバーライド、Zend_Rest_ClientにResultオブジェクトを設定したいところです。

任意のレスポンスオブジェクトを設定できるZend_Rest_Client

いまのところ、Zend_Restにそのような仕組みはないので、自作しました。

cleintクラスは、resultオブジェクトを設定するためにプロパティーとメソッドを追加。また、__call()関数内でResultオブジェクトを返す部分を修正。

<?php

require_once 'Zend/Rest/Client.php';
require_once 'Zend/Rest/Client/Result.php';
require_once 'Zend/Loader.php';

class Wads_Rest_Client extends Zend_Rest_Client
{
    protected $_resultClass = 'Zend_Rest_Client_Result';

    /**
     * @param string $result
     * @return Wads_Rest_Client
     */
    public function setResultClass($resultClass) {
        $this->_resultClass = (string)$resultClass;
        return $this;
    }

    /**
     * @return string
     */
    public function getResultClass() {
        return $this->_resultClass;
    }

    public function __call($method, $args){
        $methods = array('post', 'get', 'delete', 'put');

        if (in_array(strtolower($method), $methods)) {
            if (!isset($args[0])) {
                $args[0] = $this->_uri->getPath();
            }
            $this->_data['rest'] = 1;
            $data = array_slice($args, 1) + $this->_data;
            $response = $this->{'rest' . $method}($args[0], $data);
            $this->_data = array();//Initializes for next Rest method.

            Zend_Loader::loadClass($this->_resultClass);
            return new $this->_resultClass($response->getBody());
        } else {
            // More than one arg means it's definitely a Zend_Rest_Server
            if (sizeof($args) == 1) {
                // Uses first called function name as method name
                if (!isset($this->_data['method'])) {
                    $this->_data['method'] = $method;
                    $this->_data['arg1']  = $args[0];
                }
                $this->_data[$method]  = $args[0];
            } else {
                $this->_data['method'] = $method;
                if (sizeof($args) > 0) {
                    foreach ($args as $key => $arg) {
                        $key = 'arg' . $key;
                        $this->_data[$key] = $arg;
                    }
                }
            }
            return $this;
        }
    }
}

Yahoo api全般で使用できるresultクラス

<?php

require_once 'Zend/Rest/Client/Result.php';

class Wads_Rest_Client_Result_Yahoo extends Zend_Rest_Client_Result
{
    public function getStatus() {
        return !(bool)$this->_sxml->xpath('//Message/text()');
    }
}

楽天apiで使用できるresultクラス

<?php

require_once 'Zend/Rest/Client/Result.php';

class Wads_Rest_Client_Result_Rakuten extends Zend_Rest_Client_Result
{
    public function getStatus() {

        $status = $this->_sxml->xpath('//Status/text()');

	$status = strtolower($status[0]);
	
	if (ctype_alpha($status) && $status == 'success') {
            return true;
        } elseif (ctype_alpha($status) && $status != 'success') {
            return false;
        } else {
            return (bool) $status;
        }
    }
}

で、実際に使用するときは、以下のようになります

$client = new Wads_Rest_Client(URL);

// resultクラスを設定
$client->setResultClass('Wads_Rest_Client_Result_Yahoo');

// 楽天の場合
//$client->setResultClass('Wads_Rest_Client_Result_Rakuten');

foreach($param as $k=>$v) {
    $client->{$k}($v);
}

$res = $client->get();
if($res->isError()) {
    // エラー処理
}

// xmlの処理
...

これがいい方法なのかどうかはわかりませんが、当面はこれで運用してみます。