2012年7月29日日曜日

Bancha0.9.5 はCake2.1.5を食べるときにのむ。2.2.Xではない。

warningが出てるparseParamsでおきる+うまくいかない。Viewが無いとか言ってうまくいかない。。

(Cakeのバージョンがちがうよこれ。:後日談)


(後日追記):
CakeのDispatcherの実装がかなりかわってて
Banchaがそれにあわせて実装してあったからそもそも
うまく動きませんでした。よ。。。っていうオチです。

2.1.X系はDispatcherでinvoke呼び出しているからBanchaのinvokeが
呼び出されてバンザイなんだけど、

2.2.X系は_invoke呼び出されないし、それ以前にいろんなところで
関数のIF違うわなんだで動きません。

以下はリーディングした際のログ。
結局どうこういう以前のバージョン確認しろよお前。という
どうしようもなく目も当てられないミスを犯したのですが。。


疎通させている時の話

うまくいきません。うーまーくーいーかーなーいーよー。
帰宅後の僅かな時間しか取れない自分にとってほんのちょっとの疎通障害は
もうかなりきついのであーだこーだ言う前に急がば回れということで
ざっとソースをみてみます。。

warning発生箇所はBanchaSingleDispatchのparseParamsで第一引数はCakeRequestじゃなきゃダメだ。
みたいなこと言われてるっぽい。

perseParamのWarningは。。。

$dispatcher->dispatch($request, new CakeResponse(array('charset' => Configure::read('App.encoding'))), array('return' => true)), // second argument is expected to be overwritten by BanchaSingleDisptacher::_invoke

この第三引数のreturn=>trueが重要。
これは最後の方にBanchaSingleDispatchの_invokeで番茶用にカスタマイズしたロジックを
通らなくなってしまう。。
このarray(‘return’ => true)をrequest->param[‘return’]として追加する機構が
perseParamで行なっている

$request->addParams($additionalParams);

となります。。
さて、、、
ダンプしてみるとCakeEventがわたってきてる。
(これが例外のWarningの原因)

perseParamを追って見ることにする

grepしてみるとCakeのDispatcherのimplementedEventsに

return array('Dispatcher.beforeDispatch' => 'parseParams');

のようにあって、イベント名とメソッド名を結びつけているような実装がある。
もう一つはその設定のイベントを生成してコールしている部分ぽい実装として
BanchaDispatch::dispatch内の実装がある。
このBanchaDispatch::dispatchはbancha.phpから
呼び出されるBanchaの最初の関数(今後追加するブログのBanchaを使うところでも言っているけど)。

BanchaDispatch::dispatch内はというと、、

BanchaSingleDispach::がインスタンス化されて、そのBachaSingleDispatchのdispatchをしている。
BanchaSingleDispachはCakeのDispacherを継承しているので実際に呼ばれるdispacherはcakeのDispatcherになる。
基底Dispachでイベントが生成される(Dispatcher.beforeDispatch)。

(BanchaDispatch)  
public function dispatch(CakeRequest $request, CakeResponse $response, $additionalParams = array()) {  
    $beforeEvent = new CakeEvent('Dispatcher.beforeDispatch', $this, compact('request', 'response', 'additionalParams'));  

こんな具合で。。
ここで
CakeEventを生成してて、
ここに引き渡すのはそれぞれ、CakeEventクラスのメンバname、subject、dataに格納されるようです。。

CakeEvent->data = array($requestと$responseと$additionalParamsの配列)になる。。  

$beforeEventはEventManagerによってディスパッチされています。
イベント名で紐付けて呼び出されるみたい。。

(Dispach)  
$this->getEventManager()->dispatch($beforeEvent);  

EventManagerを掘り下げてみる。

このgetEventManager()はCakeEventManagerをインスタンス化して、
CakeEventManager->attach($this)になる。。。
この際の$thisはBanchaSingleDispatchになる。。。
BanchaSingleDispatch->Dispache->CakeEventListenerの継承関係だから。
attachのなかのif制御でinstanceof CakeEventListenerのになる
で、

(CakeEventManager->attach内)  
public function attach($callable, $eventKey = null, $options = array()) {  
   .....  
   $this->_attachSubscriber($callable);  

これが呼び出される。。callableはBanchaSingleDispatch。

attachSubscriberが呼び出される

この_attachSubscriber…は

(CakeEventManager->_attachSubscriber)  
protected function _attachSubscriber(CakeEventListener $subscriber) {  
    foreach ($subscriber->implementedEvents() as $eventKey => $function) {  
        $options = array();  
        $method = $function;  
        if (is_array($function) && isset($function['callable'])) {  
            list($method, $options) = $this->_extractCallable($function, $subscriber);  
        } elseif (is_array($function) && is_numeric(key($function))) {  
            foreach ($function as $f) {  
                list($method, $options) = $this->_extractCallable($f, $subscriber);  
                $this->attach($method, $eventKey, $options);  
            }  
            continue;  
        }  
        if (is_string($method)) {  
            $method = array($subscriber, $function);  
        }  
        $this->attach($method, $eventKey, $options);  
    }  
}  

となってて、implementedEventsは継承元のDispatcherとなるから
eventKey = ‘Dispatcher.beforeDispatch'
function = 'parseParams'
となる。

と、、
functionは文字列なので

$method = array( BanchaSingleDispatch, 'parseParams' )  

ということになる。
次にattachが呼び出されている。。。

attachが呼び出される

$this->attach($method, $eventKey, $options);  

この引数はそれぞれ、

$method = array( BanchaSingleDispatch, 'parseParams' )  
$eventKey = 'Dispatcher.beforeDispatch'  
$options = array()  

となる。。

(CakeEventManager->attach)  
   public function attach($callable, $eventKey = null, $options = array()) {  
    if (!$eventKey && !($callable instanceof CakeEventListener)) {  
        throw new InvalidArgumentException(__d('cake_dev', 'The eventKey variable is required'));  
    }  
    if ($callable instanceof CakeEventListener) {  
        $this->_attachSubscriber($callable);  
        return;  
    }  
    $options = $options + array('priority' => self::$defaultPriority, 'passParams' => false);  
    $this->_listeners[$eventKey][$options['priority']][] = array(  
        'callable' => $callable,  
        'passParams' => $options['passParams'],  
    );  
}  

listenerに登録される

ここでCakeEventManagerの_listenersに

['Dispatcher.beforeDispatch'][優先度][] = array('callable' => array(BanchaSingleDispatch,'parseParams'),'passParams'=>false)  

として登録される。
これでCakeEventMangerに登録される仕組みがわかりました。

今度は呼び出し側を追ってみます。

このdispatchの内容は
(CakeEventManager->dispatch内)

foreach ($this->listeners($event->name()) as $listener) {  
    if ($event->isStopped()) {  
        break;  
    }  
    if ($listener['passParams'] === true) {  
        $result = call_user_func_array($listener['callable'], $event->data);  
    } else {  
        $result = call_user_func($listener['callable'], $event);  
    }  
    if ($result === false) {  
        $event->stopPropagation();  
    }  
    if ($result !== null) {  
        $event->result = $result;  
    }  
    continue;  
}  

となってて、気持ちとしては'passParams === trueの制御が効いてほしいんだけど、
前述したように、_listenersにはpassParamsがfalseで登録されているから、
elseのほうに行って

$result = call_user_func($listener['callable'], $event);  

が呼び出されて、parseParamsの引数はCakeEventとしてよびだされているのではないか。

すこしだけ。Banchaを変更してみる。試しにperseParamをCakeEventを受け取る形のものを実装してみます。。

public function parseParams(CakeEvent $event) {  
    if (!empty($event->data['additionalParams'])) {  
        $event->data['request']->addParams($event->data['$additionalParams']);  
    }  
    return $request;  
}  

これだけど、そもそもCakeのDispatchの制御がそもそも違う。

Cake2.1.0だと
dispatchでthis->_invokeとコールされていて、そのさきはBanchaSingleDispatcherとなって
そのなかでreturnした結果をそのままdispatcherの戻り値として返却するからdispatcherとしての
制御ってそこで終わってた。。

Cake2.2.1だと
そもそも_invokeすら呼び出されていない氏ね。

で、ようやっとひと通りの大雑把な流れはつかめた

あれ?詰んだらしい。www
そもそもイベントリスナへ登録する時passParam =がfalseとして登録されてて、
途中どこにも更新される点がない点で詰んでる。

あれ?これはおかしくないかということで過去のソースを追ってみると
あ、あ、あ、あ、あ、あ
これならBanchaの_invokeも動くわな。。という実装。2.1.5で確認してみました。

このあたりの動きは そもそもこれはCakeの実装に依存している箇所だからね。
Cakeのバージョン2.1.0だとテストしたということ書いてあったけどね。
そりゃ2.2系はまだか。

お陰でBanchaと少し友だちになれましたww

ざんねん。
貴重な勉強時間がごっそりと削られました(まぁ、これも勉強になりましたが)。

0 件のコメント: