再現が確認できなかったため下記コマンドにより本問題を解決した修正を取り除いて同じ問題を発生させ,その問題を根本的に解決する方法を模索する.
$ git revert 91ab72c82aac2f5a0157da43b9bc1d9cab673485
$ git revert 9048766dd64fba33ceb884ada476f4e8419b4843
発生する例外は sfPluginException
である.エラーメッセージは下記の通り.
Unable to initialize channel "Unknown channel: pear.symfony-project.com"
修正方針考察¶
plugin/list のページにアクセスすると apps/pc_backend/modules/plugin/actions/actions.class.php
の 34 行目 executeList()
メソッドが呼び出される.ここで,リクエストにはパラメータが与えられていないため $this->typo には'application' が入る.次に opInstalledPluginManager
インスタンスが生成され,40, 44,48 行目と条件が変わって行き,最終的には 50 行目の処理が実行される.例外はここから投げられる.ここで 50行目が正常動作するならば $this->plugins に入っているべきなのはクラスの名前とメソッド名から分かる通り, インストールされたプラグインのリストである.このアクションでは,すべてのプラグインのリストの表示および,リストからアクティベートするプラグインを設定することができるようにすることである. このとき opInstalledPluginManager
が取得を行うべきなのは,あくまでもプラグインのリストであり,情報が取得できないなどの例外は list として与える際の詳細データに表すべきであると思う.この時点では opInstalledPluginManager
が呼び出すメソッド内で例外は処理すべきであると提案する.
処理を opInstalledPluginManager()
のなかに移す. $this->type が application の状態では getInstalledApplicationPlugins()
メソッドが呼び出されるが 何れにせよメソッド内で getInstalledPlugins()
メソッドが呼び出される.このとき,この getInstalledPlugins()
メソッドに期待すべきなのは インストールされたプラグイン全部のリストである.そして アクションから呼び出されるメソッド群がその返り値を決定するために必要な type の情報を少なくともふくんでいる必要がある.例外自体は getInstalledPlugins()
メソッドから投げられるので, ここでは 少なくとも type 情報を含んだ,プラグインインスタンスすべてのリストが返ってくるべきであるとして getInstalledPlugins()
メソッド内で例外は処理されるべきであると提案する.
処理をさらに getInstalledPlugin()
のなかに移す. この中ではコンテキストの設定インスタンスから getAllOpenPNEPlugins()
メソッドを呼び出している. getInstalledPlugin()
メソッドのなかでさらに getAllOpenPNEPlugins()
メソッドを呼び出しているという点が腑に落ちないが,処理を見る限りではおそらくインスタンス自体ではなくインスタンスと紐付くキーとなる集合が得られるものと思われる.実際に,例外を投げるのはキーから得られる getPluginInstance()
メソッドであるため, getAllOpenPNEPlugins()
から得られる情報が 「インストールされたプラグインのリスト」と等価であり, getPluginsInstance()
メソッド内で type を決定するのに有効な処理が行われないのであればここで例外を拾うことが適切であるという選択ができる.つまり,例外を処理する候補の一つと言える.このとき type の情報が得られない状態である場合を考慮する必要があるが,これは getPluginInstance()
内でそのような情報が得られないことが明らかであるときに考えるべきものとして,議論を後回しにする.
getPluginInstance()
では opPlugin
の static な getInstance() メソッド を呼び出している.opPlugin::getInstance() は Flyweight パターン を用いているように見える. opPlugin クラスが Factory と生成されるオブジェクトそのものの両方を表しているようである.opPlugin オブジェクトのインスタンスを得るために必要な情報は,プラグインのディレクトリにある package.yml ファイルから取得できるものか,チャンネルサーバから得られる情報を用いている.今回の問題において発生しうる状態は,後者において確認しようとするチャンネルサーバが symfony 側でハードコーディングされており,登録されていない場合に例外を投げるようである.取得ができないという状態ならば 情報なしとして opPlugin オブジェクトのインスタンスが完了するように実装すべきであると思い,この部分について実装を進める方針とする.
本問題とは関係がないといえば無いが, opPluginManager のように Manager という名前がついているものの,特に広い範囲でのマネージをしているわけではなくチャンネルサーバとのやり取りやその管理を行なっている実装を鑑みると,そのクラス名は正確にその役割を表していないのではないかと感じた.親クラスの sfSymfonyPluginManager については Channel という文言を用いていないため内部実装をごっそり変更するといった使い方ができると感じたが opPluginManager についてはチャンネルサーバに依存したメソッドがあるため Manager よりも Channel というように内部実装を表した名前が適当であると思われる.