DelegateのCallされる順番

428 views
Skip to first unread message

ITOH Takashi

unread,
May 7, 2008, 7:48:42 AM5/7/08
to xcube-...@googlegroups.com
伊藤です。

Delegateって、Preloadで登録しますけど、実際にCallされるのは
どのタイミングかって分かりにくいですよね・・・。

PreBlockFilterで登録したのに、PostFilterで登録したDelegateより
後で実行されるのは何故・・・と小一時間悩んだりしてしまいました。

この辺、DelegateNameがどの順番でCallされるのかわからないかなーと
core/XCube_Delegate.class.php
にちょいちょいとデバグプリントを入れてみたのですが、
XCube_Delegate::call()
で断念してしまい・・・。

伊藤

minahito

unread,
May 7, 2008, 5:52:55 PM5/7/08
to xcube-...@googlegroups.com
minahito です。

デリゲートはありていに言えば複数の関数ポインタを溜めてマルチキャストにコールバックする
仕組みですので、(.NETなどでは)元来実行順制御はありません。

XCube_Delegate はこの点を拡張しており、register() の第二パラメータにプライオリティを指定
することで実行順を制御することができます。

XCUBE_DELEGATE_PRIORITY_1 ~ XCUBE_DELEGATE_PRIORITY_10
の定数があり、パッケージ標準のデリゲートは特殊なものを除けば、すべて
XCUBE_DELEGATE_PRIORITY_5 で登録されているはずです。

標準のものより先や後で処理しないと実行がおかしくなる場合は 5 より優先度の高い定数を
第二パラメータに入れてやると先に実行されるようになります。
同じ優先度のコールバック登録の実行順序に関しては未定義(不定)です。

ってな感じになっております。(^^)


2008/05/07 20:48 ITOH Takashi <tohok...@gmail.com>:

--
minahito (mina...@gmail.com)

ITOH Takashi

unread,
May 7, 2008, 9:05:38 PM5/7/08
to xcube-...@googlegroups.com
minahitoさん、ありがとうございます。

minahito さんは書きました:


> XCube_Delegate はこの点を拡張しており、register() の第二パラメータにプライオリティを指定
> することで実行順を制御することができます。

はい、ひとつのDelegateの中で、実行関数のプライオリティ順は
あるのですが、Delegate自身の実行順序は何かわかりようが無いかなーと。


手元に、XOOPSCube Dev.バイブルがあるのですが、そのP.231~232に
Delegate一覧があるのですが、
「Legacy_Util.CreateModule」 は、 「Legacy_RenderSystem.SetupXoopsTpl」 の前に実行される・・・
とかそういうのです。


なんでそんなことを知りたいと思ったかというと、
Legacy_RenderSystem.SetupXoopsTpl
に、xoops_version.phpの内容を反映させた処理をさせたいと思ったのですが、

Legacy_Util.CreateModule
Legacy_RenderSystem.SetupXoopsTpl
Legacy_Util.CreateBlockProcedure
の順で(逆だったかな?) Delegate->call()されるので、Blockの方が
間に合わないなーと。

であれば、更にその後でCallされるDelegateで処理させようと思ったのですが、
それが見つからない・・・・という状況で。。

伊藤

minahito

unread,
May 8, 2008, 9:04:32 AM5/8/08
to xcube-...@googlegroups.com
minahito です。
なるほど勘違いしていました (^^;;

> あるのですが、Delegate自身の実行順序は何かわかりようが無いかなーと。

これはソースを読む以外ではシーケンス図を起こさないとつかめないと思います。。

イメージ的には、

class A
{
function B() {}
function C() {}

function D()
{
$this->B();
$this->C();
}
}

とあるものを、

class A
{
function B() {}
var $mC;

function D()
{
$this->B();
$this->mC->call();
}
}

と書き換えたのが、デリゲートですので、
「デリゲートに絞った実行順」
というより、考え方的には「処理のシーケンスがどうなっているのか」という感覚で
見ることになると思います。

デリゲートオブジェクトは自分の登録名を知らないので、 XCube_Delegate にブレー
クポイントやprintfをしかけても追えません。

Legacy_RenderSystem.SetupXoopsTpl はちょっと特殊で、
$xoopsTpl が早い段階で作られていないと互換性がとれないため、 Package_Legacy
には、 getRenderSystem("Legacy_RenderSystem") とだけコールして、レンダーシス
テムが本来必要ではない時期にさっさとレンダーシステムを生成して $xoopsTpl を作る
という回避策がとられています。

(XOOPS Cube は考え方的にはレンダリング直前までレンダーシステムを必要としない
 しかし X2 モジュールは common 終了後に $xoopsTpl があるものとして書かれている
 ものがあるため、特殊な回避策を仕込む必要があった)

したがって、 Legacy_RenderSystem.SetupXoopsTpl がコールされるタイミング……
すなわち Legacy_RenderSystem::Legacy_RenderSystem() が実行されるタイミングは、
厳密には不定ですが、恐らく大半の Package_Legacy のサイトでは、この回避策用の
モジュールプリロード StartupXoopsTpl が実行されたタイミングになると思います。

http://xoopscube.cvs.sourceforge.net/xoopscube/Package_Legacy/html/modules/legacyRender/preload/StartupXoopsTpl.class.php?view=markup

"一部の XOOPS2 リソースは、Legacy_RenderSystemがXOOPS Cubeの本来の
プロセス下で準備されるより前に $xoopsTpl を必要とします。そのために、このアク
ションフィルターはダミーとして Legacy_RenderSystem を取得しようと試みます。"

2008/05/08 10:05 ITOH Takashi <tohok...@gmail.com>:

--
minahito (mina...@gmail.com)

tohokuaiki

unread,
May 8, 2008, 10:03:54 AM5/8/08
to XOOPS Cube Developers Group Japan
伊藤です。


On 5月8日, 午後10:04, minahito <minah...@gmail.com> wrote:
> これはソースを読む以外ではシーケンス図を起こさないとつかめないと思います。。

ですよね。シーケンス図の勉強がてら作ってみるかな・・・

> デリゲートオブジェクトは自分の登録名を知らないので、 XCube_Delegate にブレー
> クポイントやprintfをしかけても追えません。

やっぱりそうですか。
この辺、ダメそうだったので、コールされるClass::Functionだけでも分からないかなーと
ちまちまとやってましたが、それ分かってもその先が遠そうで断念してました。



> Legacy_RenderSystem.SetupXoopsTpl はちょっと特殊で、
> $xoopsTpl が早い段階で作られていないと互換性がとれないため、 Package_Legacy
> には、 getRenderSystem("Legacy_RenderSystem") とだけコールして、レンダーシス
> テムが本来必要ではない時期にさっさとレンダーシステムを生成して $xoopsTpl を作る
> という回避策がとられています。
>
> (XOOPS Cube は考え方的にはレンダリング直前までレンダーシステムを必要としない
>  しかし X2 モジュールは common 終了後に $xoopsTpl があるものとして書かれている
>  ものがあるため、特殊な回避策を仕込む必要があった)

なるほど!!
私も考え方としては、「レンダラー($xoopsTpl)は最後までnewされないだろう」っていう
予想のもとにソース追ってたので、このpreloadは盲点でした。

ありがとうございます。解決にはなってないのですが、とりあえず現状では
できないってことはわかりました(笑)。

K. Ono

unread,
May 8, 2008, 12:07:42 PM5/8/08
to xcube-...@googlegroups.com
小野です。

似たようなことに関して前にちょっと書いたのですが、

http://xoopscube.jp/modules/xigg/index.php/node/64?comment_id=83#comment83

要は、delegateの登録(register)とdelegateの実行(call)はdelegate managerに
全て任せてしまえば少し見通しがよくなるかもなあと。シグネチャのチェックを行っている
delegateもUser_UserDeleteAction::_mDoDeleteのみのようでしたので。。

XCube_Delegate::call()だと実行されているデリゲート名が分からないので、これを
XCube_DelegateManager::call()を作成してデリゲート実行時は必ずこのメソッド経由
にするか、またはXCube_DelegateUtils::call()経由にするかに統一すると、実行されている
デリゲート名が追いやすくなる(XCube_DelegateManagerは全てのデリゲート名を
把握しているはずなので)かなあ。。と

のぶのぶ

unread,
May 8, 2008, 9:32:34 PM5/8/08
to xcube-...@googlegroups.com
のぶのぶです。

> 要は、delegateの登録(register)とdelegateの実行(call)はdelegate managerに
> 全て任せてしまえば少し見通しがよくなるかもなあと。

確かに、ソースの追いやすさだけを考えるのであれば、すべてmanager経由の方が楽かもしれません。

XCLにおけるDelegateの導入の経緯については、旧XCubeDev MLでの話しになりますが・・・

発端) 以下にして、X2のHackに相当する部分を、コアに手に入れることなく実現する方法を提供するか?
アプローチ1) イベントによるフックポイントを設け、EventManagerによって制御 XCL2.1 Alpha1時代
アプローチ2) C#のdelegate(メソッドの委譲)の概念をまねて、PHPに実装して、XCL内のクラスメソッドの
         換装と拡張を可能に → XCL2.1 Alpah2以降?

という経緯の中で、最初にEventモデルがあったので、その継続のためにDelegateManagerという形での
大域名をもつDelegateの定義・呼出方法が残ったということだと、小生は理解しています。

XCLでは実質的にSingletonのクラスが多いため、class内Delegateメンバーと、大域Delegateの
区別をする必要があるシーンが少ないですが、これらはきちんと区別して利用されるべきだと
考えます。
現状その区別の基準がすべて揃っているか?という議論は別です。パフォーマンスの面で、
DelegateManagerのオーバーヘッドを回避するためにDelegate直使用なんてシーンも
あるかもしれませんし・・・・

但し、class内Delegateメンバーについて、検知しづらいというのは、良くわかるので、
メンバー名のネーミングについて、すべて mdで開始するとかのルールを別途設けて
おいたほうが良かったかなとは思います。

K. Ono

unread,
May 8, 2008, 10:56:30 PM5/8/08
to xcube-...@googlegroups.com
小野です。

> XCLでは実質的にSingletonのクラスが多いため、class内Delegateメンバーと、大域Delegateの
> 区別をする必要があるシーンが少ないですが、これらはきちんと区別して利用されるべきだと
> 考えます。
> 現状その区別の基準がすべて揃っているか?という議論は別です。パフォーマンスの面で、
> DelegateManagerのオーバーヘッドを回避するためにDelegate直使用なんてシーンも
> あるかもしれませんし・・・・

なるほど、そうですか。この辺りやはりclass内Delegateと大域Delegateとを明確に
分けられれば良いですね。

どちらかと言うと、前者が純粋なDelegateで後者はEventというイメージなので、
そういう意味ではそれぞれ下記のような定義になるでしょうか。。

Delegate(class内Delegate)
・ある1処理に関しての全ての権限を委譲する仕組み
・移譲先が必須となる。
・移譲先が複数であった場合に問題は起こらないのか?

Event(大域Delegate)
・ある事象(イベント)が発生したことを通知する仕組み。
・通知先はあってもなくても構わない。
・通知先はいくつでも可。

minahito

unread,
May 8, 2008, 11:30:19 PM5/8/08
to xcube-...@googlegroups.com
minahito です。
すみません、 onokazu さんと nobunobu さんへのレスは長くなるので昼休みには書けないので (^^;;
こちらだけ:

>> これはソースを読む以外ではシーケンス図を起こさないとつかめないと思います。。
>
> ですよね。シーケンス図の勉強がてら作ってみるかな・・・

すみません、コレ、よく考えたら XCube_Delegate::call() にブレークポイントをしかけて、
(Managerを経由した呼び出しでは)コールスタックから XCube_DelegateManager::call を選んで
その中で呼び出し名を調べるという作業を繰り返せば、デリゲートの呼び出し順のみをすばやく
書き出せることに気づきました。

もしくは、コールスタックを見るだけでも呼び出し元(ソースで使われているだいたいの位置)は分かる
と思うので、該当するソースを見て、そこから書き出してもいいと思います。

2008/05/08 23:03 tohokuaiki <tohok...@gmail.com>:

--
minahito (mina...@gmail.com)

ITOH Takashi

unread,
May 9, 2008, 7:19:44 AM5/9/08
to xcube-...@googlegroups.com
伊藤です。

minahito さんは書きました:


> minahito です。
> すみません、 onokazu さんと nobunobu さんへのレスは長くなるので昼休みには書けないので (^^;;
> こちらだけ:
>
>>> これはソースを読む以外ではシーケンス図を起こさないとつかめないと思います。。
>> ですよね。シーケンス図の勉強がてら作ってみるかな・・・
>
> すみません、コレ、よく考えたら XCube_Delegate::call() にブレークポイントをしかけて、
> (Managerを経由した呼び出しでは)コールスタックから XCube_DelegateManager::call を選んで
> その中で呼び出し名を調べるという作業を繰り返せば、デリゲートの呼び出し順のみをすばやく
> 書き出せることに気づきました。

これって、XCube_DelegateManager::call()がある前提ですが、小野さんの
http://xoopscube.jp/modules/xigg/index.php/node/64?comment_id=83
で提案されてる XCube_DelegateManager::call() って未実装というか、現状で
DelegateManagerからDelegate::call()して実行ってことは無いですよね?


> もしくは、コールスタックを見るだけでも呼び出し元(ソースで使われているだいたいの位置)は分かる
> と思うので、該当するソースを見て、そこから書き出してもいいと思います。

これって、Delegate::call()で _mCallbacks プロパティを調べてってことですよね。
このあたり、どうやってRegisterしてAddして _mCallbacksに詰めてるのかよくわかって
ないので、詳しくソースみてみます。

伊藤

minahito

unread,
May 9, 2008, 7:53:51 PM5/9/08
to xcube-...@googlegroups.com
minahito です。

> これって、XCube_DelegateManager::call()がある前提ですが、小野さんの
> http://xoopscube.jp/modules/xigg/index.php/node/64?comment_id=83
> で提案されてる XCube_DelegateManager::call() って未実装というか、現状で
> DelegateManagerからDelegate::call()して実行ってことは無いですよね?

XCube_DelegateManager への登録名を調べたいなら……という意味で書きました。
XCube_DelegateManager の役割は Proxy ですから、 XCube_DelegateManager::call() は
該当する XCube_Delegate::call() を呼んでいるだけです。

ですので XCube_Delegate::call() にブレークポイントをしかければすべてのデリゲート
の call をつかまえられます。

>> もしくは、コールスタックを見るだけでも呼び出し元(ソースで使われているだいたいの位置)は分かる
>> と思うので、該当するソースを見て、そこから書き出してもいいと思います。
>
> これって、Delegate::call()で _mCallbacks プロパティを調べてってことですよね。

分かりにくくてすみません (^^;
デバッガのコールスタックをチェックするという意味です。

そうすれば関数の呼び出し履歴が分かりますので、ソースを追うより素早く呼び出し順を調べられる
と思います。

xdebug で、コールスタックにある関数の中の変数値まで停止中にチェックできるかどうかは知らない
のですが、たぶんできると思います。
(C++でさえ最近のデバッガでは可能なので、スクリプト言語なら間違いなくできるはず)

2008/05/09 20:19 ITOH Takashi <tohok...@gmail.com>:

--
minahito (mina...@gmail.com)

minahito

unread,
May 9, 2008, 8:34:29 PM5/9/08
to xcube-...@googlegroups.com
minahito です。

> 小野です。
>
> 似たようなことに関して前にちょっと書いたのですが、
>
> http://xoopscube.jp/modules/xigg/index.php/node/64?comment_id=83#comment83

orz

すみません見落としてました。。。
しかも英訳まであるし。。。
ありがとうございます。

> 要は、delegateの登録(register)とdelegateの実行(call)はdelegate managerに
> 全て任せてしまえば少し見通しがよくなるかもなあと。

nobunobu さんが書かれているように、この機構は、
まず型である XCube_Delegate があり、さらに PHP のインタープリタ特性にプリロード
を組み合わせるための一種の Proxy である XCube_DelegateManager に仕事をさせるという
考え方になっています。

まず純粋なコールバックの統一型が欲しかったというところから始まっていますので、
この観点からいくと全てをグローバルに、かつ、「staticに」してしまう XCube_DelegateManager
は欠点があり、すべてのデリゲートがこれを通じるようになると、プログラムには大きな
制約がかかります。


PHP は仮想関数の概念が無く、すべてのオブジェクトメソッドをオーバーライドできる言
語です。

しかしクラスベースのオブジェクト指向言語ですので、生成プロセスをおさえなければオー
バーライドしたオブジェクトを得ることはできません。
他者に生成されたオブジェクトの振る舞いを変更して(←委譲してもらって)戻すというこ
とができません。

それこそアセンブリの時代からこのようなとき(それが必要なとき)には間接ジャンプのア
ドレスを書き替えられるメモリブロックを用意して、使用側でそれを書き替えていました。
いわゆるコールバックです。
これを言語的にきちんと整理したのが C# のデリゲートで、それをクラスとして導入したの
が、XCube_Delegate ということになります。

ただ、 PHP はインタプリタのため、プリロードや先行するプログラムでは、後半生成され
る(生成されるかもしれない)デリゲートにアクセスできなかったり、モジュールも深い位
置のデリゲートをカスタマイズ用に公開できないケースが多々あります。

これはユーザーの手元で初めてビルド内容が確定するという XOOPS の特性もあるかもしれ
ません。

ここで XCube_DelegateManager が必要となり、プログラム上で結合できないものを、名前
を使用して結合の代理や遅延させる仕事を担当してくれます。

それと引き換えに、 nobunobu さんが書かれているように、グローバルスコープ化するだけ
でなく、一種の static 状態になります。この static 化が非常に重い制限で、デリゲートの
誕生理由をすべて吹き飛ばすほど可用性が低下します。

事実上の static 化というのは、
onokazuさんが言われているように、デリゲートにすべて名前をつけて登録するという考え
方でまとめてしまうと、インスタンスが個別に持つデリゲートの中身がすべて同じものになっ
てしまうというものです。
個別にデリゲートを持っているというより XCube_DelegateManager の持つデリゲートのポ
インタを貰っているような状態になってしまいます。

たとえば XCube_PageNavigator のようなクラスをコアやベースで生成して貰って受け取る
場合や、クラスが final クラスで継承できない場合、これらのクラスが持つデリゲートが static
だとインスタンス毎に処理を委譲してもらって活用するという使い方ができなくなります。

こうすると純粋なコールバックの手続き統一型としては役割が果たせなくなり、前述の一般
的なプログラム手段である「コールバック」のやり方については各自バラバラということに
なってしまいます。
(作者ごとに手順が違うものになるでしょう)

何らかの方法を使って、生成される側が名前をずらして登録すれば static 化を避けることも
できますが、それはすべてのデリゲートを global & static するというデザインのアーキテク
チャにおけるバッドノウハウの領域を出ないと思います。
今のように XCube_DelegateManager の意味を理解して使うほうが健全です。


現時点では XCube_Delegate はプリロードから登録名を使ってアクセスする使い方ばかりが
目立っていますので、 XCube_DelegateManager に機能を束ねてしまおうという話が出てく
るのも分からなくはありません。
しかし純粋なコールバック手続きとしても現在使われていますし、これらの使用感が分かっ
てくれば将来はさらに使われるようになると思います。

なので僕は XCube_Delegate はあくまで単体で役割を果たす「型」であり、すべてのデリゲー
トに名前をつけて XCube_DelegateManager の下において global & static 化を行うと、意味
合いがまったく違うものになってしまうと考えます。

> シグネチャのチェックを行っている
> delegateもUser_UserDeleteAction::_mDoDeleteのみのようでしたので。。

いやぁこれ重いと言われそうだったもので (-_-;;
Cube の開発中は重い重いと Mari○uana さんに脅され続けていたので、
シグニチャの登録を避けてしまいました。


2008/05/09 10:32 のぶのぶ <nobu...@nobunobu.com>:


> 但し、class内Delegateメンバーについて、検知しづらいというのは、良くわかるので、
> メンバー名のネーミングについて、すべて mdで開始するとかのルールを別途設けて
> おいたほうが良かったかなとは思います。

確かにもう少しハンガリアンにしても良かったかもしれません。 (-_-;

2008/05/09 1:07 K. Ono <ono...@gmail.com>:

--
minahito (mina...@gmail.com)

K. Ono

unread,
May 9, 2008, 9:48:25 PM5/9/08
to xcube-...@googlegroups.com
小野です。

minahitoさん、詳細な説明ありがとうございます。

それで、先にも少し書かせてもらいましたが、delegateとeventとは仕組みは
似ていますが、その用途は全く別のものだと思います。

> Delegate(class内Delegate)
> ・ある1処理に関しての全ての権限を委譲する仕組み
> ・移譲先が必須となる。
> ・移譲先が複数であった場合に問題は起こらないのか?
>
> Event(大域Delegate)
> ・ある事象(イベント)が発生したことを通知する仕組み。
> ・通知先はあってもなくても構わない。
> ・通知先はいくつでも可。

minahitoさんが書かれたのは、おそらく前者の純粋な意味でのdelegateに
関してだと思います。インスタンス毎にそれぞれ別々のデリゲートが必要
になるという状況においては、DelegateManager経由だと確かに少し
おかしな関係になってしまいますね。

それに対して、僕がDelegateManagerに統一した方が良いという点を書いたのは
後者のグローバルなobserverもしくはevent dispatcher的な機能に関してだと
思います。

これらはやはり、それぞれ切り離して考えるべきものではないかと思います。
おそらくですが、現状ではモジュール開発者による後者の利用法がほとんどだと
思いますので、その時に各イベントの正確な発生地点および発生時期を突き詰め
にくいのは、この機能を利用する側に取ってはかなり頭の痛いものでもあります。

クラス化はある意味ブラックボックス化ですので、その役割が細分化/明確化
されていればいるほど利用側はとても楽になると思います。


>> http://xoopscube.jp/modules/xigg/index.php/node/64?comment_id=83#comment83
>
> orz
>
> すみません見落としてました。。。
> しかも英訳まであるし。。。

いえ、英訳が完了してからsf.netに出そうと思っていましたが、途中で挫折してしまいました。(^^;

minahito

unread,
May 10, 2008, 1:47:40 AM5/10/08
to xcube-...@googlegroups.com
minahito です。

> それで、先にも少し書かせてもらいましたが、delegateとeventとは仕組みは
> 似ていますが、その用途は全く別のものだと思います。
>
>> Delegate(class内Delegate)
>> ・ある1処理に関しての全ての権限を委譲する仕組み
>> ・移譲先が必須となる。
>> ・移譲先が複数であった場合に問題は起こらないのか?
>>
>> Event(大域Delegate)
>> ・ある事象(イベント)が発生したことを通知する仕組み。
>> ・通知先はあってもなくても構わない。
>> ・通知先はいくつでも可。
>
> minahitoさんが書かれたのは、おそらく前者の純粋な意味でのdelegateに
> 関してだと思います。

いやぁ~~ (^^;
Package_Legacy の DelegateManager の用途は前者も相当数ありますよ。
というか、ほっとんど前者だと思います。

後者は数えるほどしか無いと思います。

通知だけを行って、処理を依頼しない(通知先がなくても正常に動作する)
系統のものは少ないです。大半のものはデリゲートでまずクラスを作って、
プライマリプリロードで処理を突っ込んでいます。

(これは setting ファイルをいじればカスタマイズできるように)

確かに C# でも delegate を利用した event 型があるのでこの系統立ては
必然性があると思うのですが、

XCube_DelegateManager で管理するもの = イベント

には現在なっていないことを共通認識にして議論を進められればと思います。
Package_Legacyでは、

XCube_DelegateManager で管理するもの =
 95% ... デリゲートだが、遅延結合のために(カスタマイズ用に)登録している
 5% ... 小野さんのいうイベント。空っぽでも動作に支障はない。

くらいの割合で、まぁまず大半がデリゲート的用途で使われているはずです。

> これらはやはり、それぞれ切り離して考えるべきものではないかと思います。
> おそらくですが、現状ではモジュール開発者による後者の利用法がほとんどだと
> 思いますので、その時に各イベントの正確な発生地点および発生時期を突き詰め
> にくいのは、この機能を利用する側に取ってはかなり頭の痛いものでもあります。

小野さんのおっしゃっていることを自分なりに言い換えると、あくまで、

「デリゲートだろうとイベントだろうとモジュール開発者にとってはフックポイント
なので後者の利用法がほとんど」

というのが実態で、これは別に

「したがって XCube_DelegateManager に突っ込んであるデリゲートは全部イベント
 である」

ということではないと思います。

--
minahito (mina...@gmail.com)

minahito

unread,
May 10, 2008, 1:54:37 AM5/10/08
to xcube-...@googlegroups.com
minahito です。

書ききるまでに送信してしまいました……


デリゲートもオーバーライド同様、メソッドの抽象化のための方法ですが、
PHP や XOOPS の特性上遅延結合が必要なので XCube_DelegateManager のような
Proxy は必須です。

一方、イベントというのも分かるので(Package_Legacyでは数えるほどしか無いですが)
XCube_EventManager というのを作って厳密にわけても面白いと思います。

しかし、結局追うべき箇所が二箇所に分かれてしまう気もします。 (^^;

# なお現在の Package_Legacy は「イベントはデリゲートインスタンスを作らなくてOK」
# ということになっています。
# XCube_DelegateManager::call() を直呼びしているものがイベントだと考えてください。
# 検索すると分かると思いますが割合としては非常に少ないです。
# 逆に $this->mdExecute->register(...) で登録しているものは全てデリゲートで、
# イベント的な意味合いが無いものです

単純に情報が少ないということが問題であるなら、デリゲートとイベントを分けるのでは
なく、 Doxygen コメント規約を策定して、ドキュメントでフォローした方が現実味がある
と思うんですがどうでしょうか。

イベントという考え方を新たに起こす場合は、 C# 同様 delegate を活用した存在として
定義できそうですね。

(ただ、しつこいようですが、現状ではほんとーに数が少ないので注意ッス;;
 ほっとんどデリゲートです)


2008/05/10 14:47 minahito <mina...@gmail.com>:

--
minahito (mina...@gmail.com)

K. Ono

unread,
May 10, 2008, 5:29:15 AM5/10/08
to xcube-...@googlegroups.com
小野です。

> # XCube_DelegateManager::call() を直呼びしているものがイベントだと考えてください。
> # 検索すると分かると思いますが割合としては非常に少ないです。

これですが、XCube_DelegateManager:call()というのはないので、XCube_DelegateManagerから
登録されているデリゲートを取得して、それをcall()しているXCube_DelegateUtils::call()を検索
するということになるかと思います。

単純に検索した限りでは、XCube_Delegate::call()がXCube_DelegateUtils::call()より
少し多いというところでしょうか。

ただ、デリゲートとイベントの相違に関する件は、minahitoさんと少し認識が違っていた
みたいなので、これ以上の議論は止めておきます。

要は、minahitoさんもおっしゃっていますが、

> 小野さんのおっしゃっていることを自分なりに言い換えると、あくまで、
>
> 「デリゲートだろうとイベントだろうとモジュール開発者にとってはフックポイント
> なので後者の利用法がほとんど」
>
> というのが実態

とうことがそもそもの発端で、

デリゲートだろうとイベントだろうと、現状はこれらは全て外部へと公開するフックポイント
としての使い方がほとんどであるからこそ、各フックポイントの発生の順番や正確な箇所を
把握できないのはモジュール開発者側からみるときつい

それではDelegateManagerのデリゲート名を利用して、全てのデリゲートを生成/実行させるのが良いのでは?

>> デリゲートにすべて名前をつけて登録するという考え方でまとめてしまうと、インスタンスが個別に
>> 持つデリゲートの中身がすべて同じものになってしまうことがあるのでまずい。

それではそういったものは純粋なデリゲートとして、その他デリゲートとは明確に分けた方が良いのでは?
ということが言いたかったのです。

また、ソースコード中で具体的に言うと、XCube_Delegate::register()で固有のデリゲート名で
グローバルに登録されているにも関わらず、デリゲート名ではなくXCube_Delegate::call()で
デリゲートが実行されているものが状況を複雑にしているのではないかなーと思います。

K. Ono

unread,
May 10, 2008, 5:42:21 AM5/10/08
to xcube-...@googlegroups.com
小野です。

> 単純に情報が少ないということが問題であるなら、デリゲートとイベントを分けるのでは
> なく、 Doxygen コメント規約を策定して、ドキュメントでフォローした方が現実味がある
> と思うんですがどうでしょうか。

それでも良いと思うのですが、このデリゲート機構を利用して、モジュール開発者独自の
デリゲート等がどんどん増えてくることも考えると。。

デバッグ機能を有効にするだけで、実行されるデリゲートが順番にダンプ表示できるのが
開発者やユーザにとっても一番良いのではないでしょうか。

minahito

unread,
May 11, 2008, 12:44:42 AM5/11/08
to xcube-...@googlegroups.com
minahito です。

> 単純に検索した限りでは、XCube_Delegate::call()がXCube_DelegateUtils::call()より
> 少し多いというところでしょうか。

ええっ;; まじですか!?
そんなはずは……

Mari本にグローバルデリゲート一覧があるので後日ちょっと確認してみます。
イベント風の用途は数えるほどしかないはず……

> また、ソースコード中で具体的に言うと、XCube_Delegate::register()で固有のデリゲート名で
> グローバルに登録されているにも関わらず、デリゲート名ではなくXCube_Delegate::call()で
> デリゲートが実行されているものが状況を複雑にしているのではないかなーと思います。

個人的には、デリゲートインスタンスの所有とプロキシの関係を考えれば、
登録しようがしまいがインスタンスのほうを使うべきだと思います。
文字列を登録と実行の2カ所で使うのが好みじゃないというのもありますが、
登録したらマネージャを通じて、しなければインスタンスを通じて、と軸がブレるのも
あまり好みではありませんでした。

なのでこちらのほうがデリゲートというものの認識的にシンプルだと認識していたのです
が、カオスにしていたとは思いませんでした。

ちょっと考えてみます。(^^;

# ただ、別途生成してマネージャーのマップに登録したものであっても、
# 生成したものをつかんでいる存在(生成主)はそっちを使うというプログラミングスタイルは、
# デリゲートに限った話ではないので、全般的に、XOOPS Cube がこういったものに対して、
# どのようなプログラム観をもってあたっていくのかという話に繋がっていく気がします。
# 僕はポインタをつかんでいるなら、それを連絡用に他所に登録しても、
# this 内では自分がつかんでいるポインタのほうを通じてアクセスする派なので……

> デバッグ機能を有効にするだけで、実行されるデリゲートが順番にダンプ表示できるのが
> 開発者やユーザにとっても一番良いのではないでしょうか。

これはデバッガ or プロファイラではダメなのでしょうか?;;;
すみませんPHP的には素人発言になるのですが、ここは他言語ならプロファイラで見る部分
だと思います。よほどのことがあればモニタリングしますが……

現在のデリゲートは非常にプリミティブな存在ですので、
PHP側のコールスタックを覗かないとデリゲートの所有者や、デリゲートに飛ぶ前のスタック
フレームの情報は掴めません。

これは "コールバック型" という概念がある(宣言し、生成毎に使用できる)限り、こうなりま
すが、ダンプのためにそれを何かに束ねるというのも、個人的にはあまり聞かない方法です。
たとえば他言語他システムなどをみても、コールバックの計測のためにコールバックの使い方や
存在定義自体を変えてしまうというのは見たことがありません。僕が見たことが無いだけかも
しれませんが……

デリゲートはメソッドの抽象化技法のひとつですので概念的にはメソッドと同じです。
それを考えれば、デリゲートだけでなく継承拡張可能なクラスなどのメソッドのシーケンスも
掴みたいときがあると思います。

それを含めてデバッガか(僕がイメージするところの)プロファイラがあればオールオッケー
だと思うんですがどうなんでしょうか? (^^;

# こんなこと言っといて PHP のプロファイラはまったく詳しくない orz

なんかそのへん(コツ)もふくめて一回ドキュメントにしたほうがいいでしょうか。
それで不足があれば(デバッガの機能ではプログラム上不健全な問題があるのなら)拡張する
とかで……

2008/05/10 18:29 K. Ono <ono...@gmail.com>:

--
minahito (mina...@gmail.com)

K. Ono

unread,
May 11, 2008, 6:13:29 AM5/11/08
to xcube-...@googlegroups.com
小野です。

> # ただ、別途生成してマネージャーのマップに登録したものであっても、
> # 生成したものをつかんでいる存在(生成主)はそっちを使うというプログラミングスタイルは、
> # デリゲートに限った話ではないので、全般的に、XOOPS Cube がこういったものに対して、
> # どのようなプログラム観をもってあたっていくのかという話に繋がっていく気がします。
> # 僕はポインタをつかんでいるなら、それを連絡用に他所に登録しても、
> # this 内では自分がつかんでいるポインタのほうを通じてアクセスする派なので……

はい、僕もminahitoさんと同じ派です(^^;

ただ、今回の、register()でデリゲート名をグローバル公開しているデリゲートの場合、

class A
{
var $a;

function A()
{
$a =& new XCube_Delegate();
$a->register('my delegate name');
}

function doSomething()
{
$a->call();
}
}

と、

class A
{
function doSomething()
{
XCube_DelegateUtils::call('my delegate name');
}
}

とは、同じではないかなあと。
どちらかと言うと、後者の方が実際のデリゲート実行地点にデリゲート名が有るので、
フックポイントとしては分かり易いんじゃないか、、というだけのことです。
C#でのデリゲートの使用法とかとは違ってくるかもしれませんが。。

ただ、今のところ、デリゲートのregister()前にコールバックを設定する
ことができないので、preloadとかで上の例のmy delegate nameデリゲートに
コールバックをadd()できるようにするには、コードを少し変更する必要があると
思います。


>> デバッグ機能を有効にするだけで、実行されるデリゲートが順番にダンプ表示できるのが
>> 開発者やユーザにとっても一番良いのではないでしょうか。
>
> これはデバッガ or プロファイラではダメなのでしょうか?;;;
> すみませんPHP的には素人発言になるのですが、ここは他言語ならプロファイラで見る部分
> だと思います。よほどのことがあればモニタリングしますが……

はい、僕も開発者向けであればそれで良いと思います。

ただ、今後、ユーザがどんどんプリロードを追加していったり
して、その際に問題が発生した時に、少し面倒かなあと。
デバグ機能オンでデリゲート名が出てきても、その移譲先が
分からないとあまり意味がないかもしれませんが。。

ただ、見知らぬデリゲート名が表示されてたりしたら、それに
関してユーザから更に情報を提供してもらったりとか、何らかの
対応がし易くなるなるんじゃないかと思ったのですが。。

それだけの理由ですので、この辺りはお任せします(^^;

K. Ono

unread,
May 11, 2008, 6:32:17 AM5/11/08
to xcube-...@googlegroups.com
小野です。

> ただ、今のところ、デリゲートのregister()前にコールバックを設定する
> ことができないので、preloadとかで上の例のmy delegate nameデリゲートに
> コールバックをadd()できるようにするには、コードを少し変更する必要があると
> 思います。

すいません、これは既にできたみたいですね。(^^;

minahito

unread,
May 16, 2008, 11:46:17 PM5/16/08
to xcube-...@googlegroups.com
minahito です。

> ただ、今回の、register()でデリゲート名をグローバル公開しているデリゲートの場合、
>
> class A
> {
> var $a;
>
> function A()
> {
> $a =& new XCube_Delegate();
> $a->register('my delegate name');
> }
>
> function doSomething()
> {
> $a->call();
> }
> }
>
> と、
>
> class A
> {
> function doSomething()
> {
> XCube_DelegateUtils::call('my delegate name');
> }
> }
>
> とは、同じではないかなあと。

C の関数ポインタとか、 C# のデリゲートとかって、あくまで関数の形で呼ぶじゃないですか。
それを外の管理する存在に引き渡したとしても、あくまでやっぱ関数の形で呼びたいなぁというのがあります。

しかし、ようやく時間が取れたのでじっくり見てみましたが、
デリゲート系の処理でもインスタンスがそのコードに関連したメモリ上に確保できない場面(たとえば xoops_gethandler)
ではイベント的に読んでいたり、結構ばらばらですね。 (-_-;;

まぁ、 global static function の中でやろうとすれば、マネージャにインスタンスの管理を委託する以外にほかに
方法はないんですが……

あと、コールスタックを覗く方法も試してみましたが、ざっと見るには不便ですね。
全体の動きはプロファイラを使ってつかんで、あとは onokazu さんのおっしゃるように DelegateManager を交換して
出力で抑えるしかないなぁ。
(XCube_Delegate 直は今のところプロファイラでおさえてもらうとして)

この、コールバックをどう効率よくつかんでもらって、開発に使ってもらうかという情報を開示するかということに関しては、
C ライブラリなどに一日(どころか数十年の)長があると思うので、改めてそのへんも見てみて、パクれそうな整備方法が
あればパクらせてもらうつもりです。

昔から思ってましたが PHP だとデバッグ用に処理を分けたくても、リリースでそれを取り除けないので、
XCube_DelegateManager や XCube_Delegate に開発者支援用のコードを非常に埋め込みにくいですよね。

ものすごくヤキモキします。 #if defined DEBUG ~ #endif みたいに括れたら onokazu さんのアイデアで一発なんですが、
どうしても慎重になってしまう……

(まだマネージャ系に関しては交換できるのでいいですが、 final class 系がまずい)

あるいは SDK コアとそうでないコアを分けるという以前なにかで出したアイデアでもいいかもしれない。

--
minahito (mina...@gmail.com)

ITOH Takashi

unread,
May 28, 2008, 11:49:12 PM5/28/08
to xcube-...@googlegroups.com
伊藤です。

minahito さんは書きました:


> > XCube_DelegateManager への登録名を調べたいなら……という意味で書きました。
> > XCube_DelegateManager の役割は Proxy ですから、 XCube_DelegateManager::call() は
> > 該当する XCube_Delegate::call() を呼んでいるだけです。
> >
> > ですので XCube_Delegate::call() にブレークポイントをしかければすべてのデリゲート
> > の call をつかまえられます。

この方法でやってみました。

XCube_Delegate::call() の call_user_func_array する直前(5行前)に、
dlog($callback[0], $callback[1], $callback_array[1]);
って差し込んで、mainfile.phpの define('XOOPS_ROOT_PATH', '****'); 直下に
ログ用ファイルポインタとdlog関数を定義。

$_fp = fopen(XOOPS_ROOT_PATH.'/cache/dele_log.log', "a+");
function dlog($class, $method, $file)
{
static $time;
if (is_null($time)){
$time = date('Y/m/d H:i:s');
fputs($GLOBALS['_fp'], sprintf("\n-- log start [%s] %s --\n", $_SERVER['REQUEST_URI'], $time));
}


$e = '::';
if (is_object($class)){
$e = '->';
$class = get_class($class);
}

///
$find = false;
$root =& XCube_Root::getSingleton();
$dls =& $root->mDelegateManager->_mDelegates;
foreach ($dls as $d_name=>$dls2){
foreach ($dls2 as $delegate){
foreach ($delegate->_mCallbacks as $priority=>$_callbacks){
foreach ($_callbacks as $callback_filepath){
$callback = $callback_filepath[0];
$_callback_class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0];
$_callback_method = $callback[1];
if ($class==$_callback_class && $method==$_callback_method){
$find = true;
fputs($GLOBALS['_fp'], sprintf("[DelegateName] %s (priority:%d)\n", $d_name, $priority));
}
if (isset($callback_filepath[1]) && $callback_filepath[1]==$file){
fputs($GLOBALS['_fp'], sprintf(" [FILE]=>%s \n", $callback_filepath[1]));
}
}
}
}
}
if (!$find){
fputs($GLOBALS['_fp'], "[DelegateName]=>Not Found. \n");
}

$log = ' '.$class.$e.$method;

fputs($GLOBALS['_fp'], $log."\n");
}

ってすると、管理画面のユーザーグループ一覧では
-- log start [/test/xcl/Package_Legacy/html/modules/user/admin/index.php?action=GroupList] 2008/05/29 12:34:11 --
[DelegateName]=>Not Found.
Legacy_TextFilter::getInstance
[DelegateName]=>Not Found.
XCube_TextFilter::getInstance
[DelegateName]=>Not Found.
Legacy_DebuggerManager::createInstance
[DelegateName] XCube_Session.SetupSessionHandler (priority:50)
Legacy_SessionCallback::setupSessionHandler
[DelegateName] XCube_Session.GetSessionCookiePath (priority:50)
Legacy_SessionCallback::getSessionCookiePath
[DelegateName] Legacy_Controller.SetupUser (priority:50)
User_Utils::setupUser
[DelegateName] Legacy_Controller.SetupUser (priority:50)
Legacy_ThemeSelect->doChangeTheme
[DelegateName]=>Not Found.
User_ActionFrame->execute
[DelegateName] User_ActionFrame.CreateAction (priority:50)
User_ActionFrame->_createAction
[DelegateName]=>Not Found.
XCube_PageNavigator->fetchNaviControl
[DelegateName] Legacy_TextFilter.MakePreXCodeConvertTable (priority:20)
Legacy_TextFilter::makePreXCodeConvertTable
[DelegateName] Legacy_TextFilter.MakeClickableConvertTable (priority:20)
Legacy_TextFilter::makeClickableConvertTable
[DelegateName] Legacy_TextFilter.MakeXCodeConvertTable (priority:20)
Legacy_TextFilter::makeXCodeConvertTable
[DelegateName] Legacy_TextFilter.MakeXCodeCheckImgPatterns (priority:20)
Legacy_TextFilter::makeXCodeCheckImgPatterns
[DelegateName] Legacy_TextFilter.MakePostXCodeConvertTable (priority:20)
Legacy_TextFilter::makePostXCodeConvertTable
[DelegateName]=>Not Found.
User_GroupFilterForm->getTotalItems

みたいに出ました。これは、プレーンなXCLだったのでこれだけですが
HDで色々とインストールすると結構な勢いでごちゃごちゃ出ました。

所々、Not FoundでDelegateの名前を見つけられないんですが、
LazyなDelegateというやつなのかな?

伊藤

Reply all
Reply to author
Forward
0 new messages