cakePHP内でPEAR Services_Amazonのキャッシュ機能を使おうとするとエラー
気がついたら1年ぶり以上のCake関係の記事。
さて、最近またCakePHPを使ってWEBアプリを組んでいるので久々に更新します。
最近、Product Advertising APIと名前が変わったAmazonのAPI。
これを使うのに便利なライブラリとしてPEAR Services_Amazonがあります。
このライブラリを使用すると、通信結果結果を配列で取得、Amazonから取得したデータのキャッシュの実装などなどが簡単に行えます。
が、CakePHP内部で、このライブラリのキャッシュ関係の部分が動かずハマってしまったので、かつ同じようにハマる人がいそうなのでメモしておきます。
発生したエラーについて
CakePHPの、あるmodelの中で、次のようなコードを書きました
<?php require_once("Services/Amazon.php"); $amazon = new Services_Amazon($amazon_access_key, $amazon_secret_key, $amazon_associate_key); $amazon->setLocale("jp"); $amazon->setCache('file', array('cache_dir' => '/path/to/cache/dir/')); ?>
すると以下のようなエラーが発生
Fatal error: Call to undefined method Cache::get() in /usr/share/php/Services/Amazon.php on line 1361
エラーが起こっているServices/Amazon.phpの処理を見て、原因特定
1361行目のクラスの_sendRequestメソッド内の_cacheというプロパティからgetメソッドを実行しようとしているところでエラーが起こっています。
<?php $cache = $this->_cache->get($cache_id); ?>
・$this->_cacheの初期化
ということで、この_cacheプロパティに、どのようなインスタンスが設定されるのかをチェック。
397行目から、始まるsetCacheメソッド内で初期化されています。
<?php 397 function setCache($container = 'file', $container_options = array()) 398 { 399 if(!class_exists('Cache')){ 400 @include_once 'Cache.php'; 401 } 402 403 @$cache = new Cache($container, $container_options); 404 405 if (is_object($cache)) { 406 $this->_cache = $cache; 407 } else { 408 $this->_cache = null; 409 return PEAR::raiseError('Cache init failed'); 410 } 411 412 return true; 413 }
エラー処理も、きちんとしてるし問題なさそうです。
今回、発生したエラーはCache::getというメソッドが存在していねぇよっというエラーなので、$cacheのメソッド一覧を確認してみましょう。
406行目の下に次の行を追加してみます
<?php 407 print_r( get_class_methods($this->_cache) ); >?
以下が結果です。
<?php Array ( [0] => getInstance [1] => __loadEngine [2] => config [3] => engine [4] => set [5] => gc [6] => write [7] => read --略--- [20] => cakeError [21] => _persist [22] => _savePersistent [23] => __openPersistent ) >?
あ、あれ、このメソッドどこかで見たことあるぞ。。。っていうか配列の20個目,cakeとかいう文字が。。。PEARのライブラリなのに。。。
つまり、CakePHP内でもCacheという名前のクラスがあって、それがPEARのCacheクラスと競合していました。
Services/Amazon.phpはPEAR Cacheを使用したいのに、CakePHPのCacheクラスのインスタンスが生成されてしまっています。
解決方法(暫定
ということで解決方法です。
今回とった方法は、あんまりよくないかもしれないけど、PEARのライブラリを書き換えてしまいます。
1.Cache.phpをコピーしてCache_PEAR.phpを作成
まず、クラス名が被っているのがまずいのでPEAR::Cacheのクラス名を変更します。
直でCache.phpを書き換えてもいいのだけど、一応コピーをとって、そっちを書き換える方針で。
2.Cache_PEAR.phpの次の2箇所(クラス名とコンストラクタ部分)を変更
- 66行目
- 129行目
3.Services/Amazon.phpでCacheインスタンスの代入部分を変更
- 変更前
<?php 399 if(!class_exists('Cache')){ 400 @include_once 'Cache.php'; 401 } 403 @$cache = new Cache($container, $container_options); >?
- 変更後
<?php 399 if(!class_exists('Cache_PEAR')){ 400 @include_once 'Cache_PEAR.php'; 401 } 403 @$cache = new Cache_PEAR($container, $container_options); >?
結果
無事、Services_Amazonのキャッシュ部分が動きました。