2016-05-02 25 views
4

タイプセーフな汎用ログdecoratorを作成したいと思います。PHPの安全ジェネリックデコレータ

私はrepositories(インターフェース)の数を持って、そして、彼らは投げるかもしれない例外をキャッチLoggerInterfaceのインスタンスにそれらを渡した後、それらを再スローそれぞれのデコレータを必要としています。専用のデコレータを作成してそれぞれをテストすることは可能です(これはかなり面倒です(特にテストをうまくやっています)。

__callを使用してより一般的なデコレータを作成することが、最初に考えられるアプローチです。しかし、これは、それが装飾するリポジトリインタフェースを実装していないオブジェクトインスタンスになります。それは私のプロジェクトにはありません。何らかの魔法を使うなど、このインターフェースを実装していることをPHPに伝える方法はありますか?reflection

stackoverflowで「how to implement a decorator in PHP?」と「Best way to implement a decorator pattern for method result caching in PHP」と読みましたが、両方とも専用アプローチと汎用アプローチを説明していますが、どちらもタイプセーフな方法で汎用アプローチを示していません。これらの質問が投稿されてからしばらく時間がたっているので、状況が変わった可能性があります。私はPHP 7を使用しており、必要に応じてPHP 7.1を使用できます。

PHPUnit_MockObjectは、PHPUnitでおなじみのgetMockメソッドによって呼び出されるのと同じコードを使用して、インターフェイスを実装するオブジェクトを構築することができます。これはジェネリックデコレータの基礎となりうる。しかし、それは生産コードで模擬ライブラリを使用する必要があります。さらに、このライブラリinternally uses evalはその仕事を終わらせる。これは私のプロジェクトでは不適格です。

+4

神で例を参照してください、私はなぜあなたのためだけにロギングを行うカスタム例外ハンドラを作成していない私はここにPHPで – strangeqargo

+1

を型安全性についての何かを見ることは決してないだろうと思いましたか?リポジトリから例外をキャッチする場合は、特別なRepositoryExceptionをスローするようにしてください(必要に応じてより詳細な例外を拡張することができます)。デコレータは、単純な問題に対するあまりにも複雑な解決策のようです。 – jfadich

+0

興味深いことに、私はそれにもかかわらず、私が間違っていないと、リポジトリが使用されている場所で潜在的にキャッチするのではなく、例外が処理されずに泡立つようにする必要があります。その場合、例外ハンドラのアプローチは実行可能ではありません。 –

答えて

0

実際に必要なのは、必要なインターフェイスを完全に実装し、それに基づいて拡張する基本Decoratorクラスを宣言することだけです。例:

interface FooInterface { 
    function doFoo(); 
    function doMoreFoo(); 
} 

class Foo implements FooInterface { 
    public function __construct() {} 
    public function doFoo() {} 
    public function moreFoo() {} 
} 

class FooDecoratorBase implements FooInterface { 
    protected $foo; 
    public function __construct(FooInterface $foo) { $this->foo = $foo; } 
    public function doFoo() { $this->foo->doFoo(); } 
    public function moreFoo() { $this->foo->moreFoo(); } 
} 

class ExtraFooDecorator extends FooDecoratorBase { 
    public function extraFoo() {} 
} 

私が具体的に自分自身を引用することはできませんが、私は彼らがして、テスト/デバッグに設計されたような製造コードにリフレクションを使用することが悪い考えであることを尊重し、他のPHPの開発者によって言われてきました心ではなく、パフォーマンスです。

+0

私はデコレータが必要です。これは、同じインタフェースで異なるタスクではなく、異なるインタフェースに対して同じ種類のタスクを実行するという点で一般的です。私は、リフレクションを使用することは、一般に生産コードでは悪いことに同意しており、何年も前から行っていません。しかし、常に例外があります。それでも、反射のない解決法が確かに好ましい。 –

0

"Generic TypeParameters"は、(except Facebook's HHVM variation)で利用できません。これは不可能です。あなたが本当にこの道に行きたいならば、answer of @Sammitchは行く道です。

ただし、別の問題を解決しようとしています。

デコレータパターンはラッパーとして機能します。クラス自体(またはオブジェクトの状態)を変更せずに、既存のクラスに追加の機能を追加する場合は、を使用します。

あなたのデコレータは次のようになり、どのようにお考え下さい。

function DoSomeOperation() { 
    try { 
     //Do The Logic 
    catch (\Exception $ex) { 
     Logger::Log($ex); 
     throw; 
    } 
} 

しかし、おそらくデコレータ/ラッパーはPDOオブジェクトのために:簡単ではありません、あなたのリポジトリでデコレータなしにそれを行うのはなぜ

function DoSomeOperation() { 
    try { 
     return $this->decoratedObject->DoSomeOperation(); //Object is injected in Decorators Constructor 
    catch (\Exception $ex) { 
     Logger::Log($ex); 
     throw; 
    } 
} 

あなたが本当に持っているものは何ですか? (ᵔᴥᵔ)

+0

私のリポジトリの実装にログインすると、ロギングの懸念(SRP違反)で汚染されてしまいます。また、私はロガーを注入する必要があるので、リポジトリを構築するのが難しくなります。 (静的バインディングとサンプルコードのようなグローバルな状態を使用することは非常に問題です。) –

+0

これは例です。あなたは、IoCを介してLogger Interfaceを自由に注入することができます。ロギングの問題はロガーです。例外がどのように処理されるべきかをロガーに理解させたくありません。あなたが 'DoSomeOperation'をして、発生した' Exception'を処理できるなら、それを行うべきです。それは同じ懸念です。 *デコレータパターン*はSRPとは関係ありません。 @JeroenDeDauw –

+0

私はロギングコードに特定の例外を認識させることを推奨していません。伐採懸念がSRPに違反しているかどうかに関わらず、我々は同意していないようだ。議論するのではなく、私のリポジトリにロガーを注入したくないということを忘れないでください。 –

1

もう1つの方法は、実行時に装飾されたクラスを動的に作成することです。残念ながらPHP doesn't allow this out of the boxrunkit拡張子がオプションでない場合は、Doctrine ORMが何をエミュレートすることができます:発生元のクラスに

  • 使用リフレクションすべてのインターフェイスとそのメソッドを取得するには、
  • :以下の手順を行いDecoratorFactoryを持っていますすべてのインターフェイスを実装する装飾されたプロキシクラスを含むPHPコードのファイル
  • 生成されたプロキシクラスを含む
  • 元のクラスで生成されたプロキシクラスをインスタンス化し、プロキシクラスを返します。

http://www.doctrine-project.org/api/orm/2.4/source-class-Doctrine.ORM.Tools.EntityGenerator.html

+0

私が知る限り、Runkitの最新リリースはPHP 7をサポートしていません。Doctrine ORMはPHPUnit_MockObjectと似ています。文字列形式でコードを作成し、それを評価します。あなたは、必要な機能だけを提供するライブラリを知っていますか? –

関連する問題