ホームページ > 記事 > PHPフレームワーク > Laravelミドルウェアを作成するためのアイデアの分析
laravelのミドルウェアを解析するための実装原則はインターネット上にたくさんありますが、読んでも理解できない読者もいるのではないかと思いますが、作者はarray_reduce関数をどうやって使おうと考えたのでしょうか?
推奨: laravel チュートリアル
この記事は私自身の視点から始まり、私が作成者であればこのミドルウェア関数をどのように実装するか、またその見つけ方と使用方法をシミュレートします。対応する機能。
laravel ミドルウェアとは
Laravel ミドルウェアは、ロジック コードを変更することなく、元のプログラム フローを中断し、ミドルウェアを通じて処理するメカニズムを提供します。機能。たとえば、ロギング ミドルウェアは、ロジック コードを変更せずに、リクエストとレスポンスのログを簡単に記録できます。
次に、ソフトウェアの実行プロセスを簡略化しましょう。コア クラス カーネルがあり、以下はその laravel コードです。
#捕获请求 $request = Illuminate\Http\Request::capture() #处理请求 $response = $kernel->handle($request);
コードの機能は、リクエストをキャプチャしてレスポンスを返すことです。 。次に、特定の実行ロジックに配布され、結果を返すコード セグメントを示します。
$kernel->handle() メソッドの実行前または実行後にロジックを追加したい場合、どのように記述すればよいでしょうか?
$request = Illuminate\Http\Request::capture() function midware(){ before()#在之前执行的语句集合 ##### $response = $kernel->handle($request); ##### after()#在之后执行的语句集合 }
このように書くのは明らかに問題ありませんが、スケーラビリティがありません。何かを実行するにはこのメソッドを変更する必要があります。これをコアコンテンツにカプセル化することは不可能です。フレームワーク。改善方法
実行するミドルウェアクラスを定義し、before()とafter()の2つのメソッドを実装します。
#配置项中有一项配置中间件: middleware = ''; $request = Illuminate\Http\Request::capture() function midware(){ middleware.before() ##### $response = $kernel->handle($request); ##### middleware.after() }
問題は解決しますか?変更する必要がないという問題は解決します。しかし、複数のミドルウェアが必要な場合はどうなるでしょうか?最も簡単に考えられるのは、ミドルウェア配列 middleware_arr を定義し、各ミドルウェア クラスを定義することです。コードは次のとおりです:
#配置项中有middleware_arr middleware_arr=array(); $request = Illuminate\Http\Request::capture() function midware(){ foreach(middleware_arr as middleware){ middleware.before() } ##### $response = $kernel->handle($request); ##### foreach(middleware_arr as middleware){ middleware.after() } }
少し古いですが、問題は解決します。しかし、これにはまだ問題があります。つまり、パラメータをミドルウェアにどのように渡すかということです。次のようにしても問題ありません:
$request = Illuminate\Http\Request::capture() function midware(){ foreach(middleware_arr as middleware){ middleware.before($request) } ##### $response = $kernel->handle($request); ##### foreach(middleware_arr as middleware){ middleware.after($response) } }
これで問題は解決したように見えますが、注意深く分析すると、次のようになります。指定されるたびに、ミドルウェアはすべて元の $request であることがわかりますが、これは明らかに機能しません。次のように変更します:
$request = Illuminate\Http\Request::capture() function midware(){ foreach(middleware_arr as middleware){ $request = middleware.before($request) } ##### $response = $kernel->handle($request); ##### foreach(middleware_arr as middleware){ $response = middleware.after($response) } }
もう 1 つの質問は、2 つのミドルウェア A と B があると仮定すると、何をするかということです。
$request = Illuminate\Http\Request::capture() $request = A.before($request); $request = B.before($request); $response = $kernel->handle($request); $response = A.after(); $response = B.after();
これは合理的ですか? 判断するのは簡単ではありません。リクエストとレスポンスのログを記録するミドルウェアがあると仮定します。現時点では、どこに配置しても記録することはできません。最初のリクエストと最後のリクエストのログを完全に記録します。同様の状況で 2 つのクラスを作成する必要がありますか? 1 つはリクエストを記録してミドルウェア配列の最初に配置し、もう 1 つは応答を処理して配列の最後の場所に配置します。要件を満たすように、後続の foreach を実行する前に middleware_arr 配列を反転することをお勧めします:
$request = Illuminate\Http\Request::capture() $request = A.before($request); $request = B.before($request); $response = $kernel->handle($request); $response = B.after(); $response = A.after();
しかし、この時代遅れで柔軟性のないソリューションに対して、より良い解決策はないのかと疑問にも思い始めました。この実行シーケンスを観察すると、ラッパー スタイル (オニオン スタイル) であることがわかりました。次の問題に対する、より柔軟で洗練された解決策を見つけることはできますか? 上の構造を見ると、いつも少し見覚えがあるように感じます。A の関数が B の関数をラップし、B の関数には最初の関数が含まれているのとよく似ています。実行コード。関数内で関数を呼び出すことは簡単ですが、ここでの各ミドルウェアは他のミドルウェアの存在を知らないため、他のミドルウェアで実行する関数を上位に渡す必要があります。 array_reduce()、
array_reduce 関数定義:mixed array_reduce ( array $input , callable $function [,mixed $initial = NULL ] )
<?php function rsum ( $v , $w ) { $v += $w ; return $v ; } function rmul ( $v , $w ) { $v *= $w ; return $v ; } $a = array( 1 , 2 , 3 , 4 , 5 ); $x = array(); $b = array_reduce ( $a , "rsum" ); $c = array_reduce ( $a , "rmul" , 10 ); ?> #输出: 这将使 $b 的值为 15, $c 的值为 1200(= 10*1*2*3*4*5)
array_reduce() はコールバックで動作します入力配列内の各セルに対して関数を繰り返し実行することで、配列を単一の値に削減します。複数の関数を 1 つの関数にラップし、最終的に呼び出されます。
#我们先假设只有一个middleware,叫log来简化情况,这里的类应该是一个类全路径,我这里就简单的写一下,要不然太长了。 $middleware_arr = ['log']; #最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。 $default = function() use($request){ return $kernel->handle($request); } $callback = array_reduce($middleware_arr,function($stack,$pipe) { return function() use($stack,$pipe){ return $pipe::handle($stack); }; },$default); # 这里 callback最终是 这样一个函数: function() use($default,$log){ return $log::handle($default); }; #所以每一个中间件都需要有一个方法handle方法,方法中要对传输的函数进行运行,类似如下,这里我类名就不大写了 class log implements Milldeware { public static function handle(Closure $func) { $func(); } } #这里不难看出可以加入中间件自身逻辑如下: class log implements Milldeware { public static function handle(Closure $func) { #这里可以运行逻辑块before() $func(); #这里可以运行逻辑块after() } }
このように、コールバック関数を実行するときの実行順序は次のとおりです。
最初に log::haddle() メソッドを実行し、
ログを実行します::before() メソッド
デフォルトのメソッドを実行し、$kernel->handle($request)を実行します
log::after() メソッドを実行します
次に次のように複数の状況をシミュレートします:
$middleware_arr = ['csrf','log']; #最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。 $default = function() use($request){ return $kernel->handle($request); } $callback = array_reduce($middleware_arr,function($stack,$pipe) { return function() use($stack,$pipe){ return $pipe::handle($stack); }; },$default); # 这里 callback最终是 执行这样: $log::handle(function() use($default,$csrf){ return $csrf::handle($default); });
実行シーケンスは次のとおりです:
1. 最初に log::haddle (csrf::handle クロージャー関数を含む) メソッド
# を実行します。 ##2. log::before () メソッドを実行します。3. クロージャを実行すると、$csrf::handle($default) が実行されます。4. csrf::before() を実行します。 method 5. デフォルトのメソッドを実行し、$kernel->handle($request)6. csrf::after() メソッドを実行します7。 log::after() メソッドを実行します ここでのもう 1 つの問題は、ミドルウェアによって生成された結果が転送されないことです。共有リソースを変更することで同じ目的を達成できます。実際に実行する必要はありません。値を次のミドルウェアに渡します。 これでこのドキュメントは終わりですが、実際には、この記事を書いたときに初めて判明した関節も多くあります。特に、クロージャ関数の使い方と理解が深まりました。クロージャ関数はリソースの使用を遅らせる可能性があります。たとえば、現時点で実行に適していないステートメントを後で渡す必要があります。クロージャは使用できます。これは、従来の関数では実行できないことです。以上がLaravelミドルウェアを作成するためのアイデアの分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。