DIYミニフレームワーク

最近、 Silexミニフレームワークについて読んだ後、私は考えました:それについて何がそんなに複雑なのですか? 私は似たようなものを書き込もうとしましたが、とても簡単でした。



このようなミニフレームワークは通常、mod_rewriteの次のルールに基づいています。

Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  1. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  2. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  3. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  4. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  5. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  6. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >



  7. Copy Source | Copy HTML < IfModule mod_rewrite . c > RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L] </ IfModule >





それらは、フレームワークが接続されているindex.phpにリクエストをリダイレクトし、このリクエストはコールバック関数で処理されます。 そのため、まず、アドレスを処理する簡単な関数を作成します。

Copy Source | Copy HTML



  1. 関数リクエスト( $パス$コールバック
  2. {
  3. if$ path == $ _SERVER [ 'REQUEST_URI' ]) return call_user_func( $ callback );
  4. }


呼び出されると、関数はユーザーがアクセスしたアドレスをチェックし、関数に渡されたアドレスと一致する場合、コールバックを呼び出します。 これはすでに基本的な機能と見なすことができます。 ただし、アドレスから変数を抽出するという必要なものはありません。 さらに、 example.com / somethingexample.com/somethingのアドレスは、関数によって異なると見なされます。 これを解決するには、アドレスを解析してarray_filterを使用します。

Copy Source | Copy HTML



  1. 関数 array_filter_callback_no_empty_str( $ value
  2. {
  3. return $ value != '' ;
  4. }
  5. 関数リクエスト( $パス$コールバック
  6. {
  7. //コールバックに渡す変数をリクエストします
  8. $ args = array ();
  9. //ユーザーがアクセスしたアドレス(URI)を部分に分割します
  10. $ uri = explode( '/'$ _SERVER [ 'REQUEST_URI' ]);
  11. //クエリパスで同じことを行います
  12. $ path = explode( '/'$ path );
  13. //両方の配列の空の部分を削除します
  14. $ uri = array_values(array_filter( $ uri 、array_filter_callback_no_empty_str));
  15. $ path = array_values(array_filter( $ path 、array_filter_callback_no_empty_str));
  16. // URIのパーツ数とパスが異なる場合、終了
  17. if (count( $ uri )!= count( $ path ))
  18. falseを返します
  19. //リクエストパスのすべての部分を調べます
  20. for$ i = 0 ; $ i <count( $ path ); $ i ++)
  21. {
  22. //パスの指定された部分が変数であるかどうかを確認します
  23. //変数パスは中括弧で囲まれ、正規表現をチェックします
  24. if (preg_match( '| ^ \ {(。*)\} $ |'$ path [ $ i ]、 $ match ))
  25. {
  26. //ある場合、この変数を配列に追加します
  27. $ args [ $ match [ 1 ]] = $ uri [ $ i ];
  28. }
  29. 他に
  30. {
  31. //リクエストの一部が変数ではない場合、URIとリクエストを比較します
  32. //一致しない場合、終了します
  33. if$ uri [ $ i ]!= $ path [ $ i ])
  34. falseを返します
  35. }
  36. }
  37. //すべてのチェックの後、コールバックを呼び出し、リクエスト変数を含む配列を渡します
  38. return call_user_func_array( $ callback$ args );
  39. }


これで、次のようなコードを作成できます。

Copy Source | Copy HTML



  1. 関数 Hello( $ who
  2. {
  3. print "Hello、$ who" ;
  4. }
  5. リクエスト( '/ hello / {who}' 、こんにちは);


それでは、Silexを見て、何が欠けているのか見てみましょう。 まず第一に、これらはアサーション(正規表現に対するパス変数のチェック)、呼び出しメソッド(GET、POST、PUTなど)およびオブジェクト指向モデルのチェックです。 最初の2つは非常に簡単に追加できます。チェックをいくつか追加するだけです。

Copy Source | Copy HTML



  1. 関数リクエスト( $ method$ path$ callback$ asserts = array ())
  2. {
  3. //サーバーにアクセスする方法を確認します。
  4. //特定のメソッドがリクエストで指定され、使用されたメソッドと一致しない場合、終了します
  5. if$ method != '' && strtolower( $ _SERVER [ 'REQUEST_METHOD' ])!= $ method
  6. falseを返します
  7. <...>
  8. //パスの指定された部分が変数であるかどうかを確認します
  9. if (preg_match( '| ^ \ {(。*)\} $ |'$ path [$ i]、 $ match ))
  10. {
  11. //存在する場合は、テストする正規表現があるかどうかを確認し、
  12. //そして、もしあれば、URIの対応する部分への準拠をチェックします
  13. if (! isset$ asserts [ $ match [ 1 ]]))|| preg_match( $ asserts [ $ match [ 1 ]]、 $ uri [$ i]))
  14. {
  15. //すべてが正しい場合、この変数をその値とともに配列に追加します
  16. $ args [ $ match [ 1 ]] = $ uri [$ i];
  17. }
  18. 他に
  19. {
  20. //値が正規表現と一致しない場合、終了します
  21. falseを返します
  22. }
  23. }
  24. <...>
  25. }


ただし、2番目も非常に簡単です。 $メソッド、$パス、$コールバックをコンストラクタに渡し、アサートのために別個のラッパー関数を作成し、すべての作業をrun()関数に入れます。上記の変数の代わりに$ this->を使用することを忘れないでください。

Copy Source | Copy HTML



  1. クラスリクエスト
  2. {
  3. パブリック $メソッド ; //リクエストメソッド(GET、POST、PUTなど)
  4. パブリック $パス ; //リクエストパス
  5. パブリック $コールバック//コールバック
  6. public $ asserts = array (); //パス変数をチェックするための正規表現
  7. //クラスコンストラクター
  8. パブリック関数 __construct( $ method$ path$ callback
  9. {
  10. $ this-> method = strtolower( $ method );
  11. $ this- > path = $ path ;
  12. $ this-> callback = $ callback ;
  13. }
  14. // re regexを追加して、nameという名前のパス変数をテストします
  15. パブリック関数 assert( $ name$ re
  16. {
  17. $ this-> asserts [ $ name ] = $ re ;
  18. //クラスの現在のインスタンスを返します
  19. //これにより、同様のコードを記述できます。$ reg-> assert( 'id'、 '| ^ \ d + $ |')-> run();
  20. $ thisを 返します。
  21. }
  22. //リクエスト処理関数
  23. パブリック関数 run()
  24. {
  25. <...>
  26. }
  27. }


2点を除いて、すべてが美しいようです。 最初のポイントは純粋に美的です-1つのリクエストを処理するには、2行のコードを書く必要があります。

Copy Source | Copy HTML



  1. $ req = new Request( '/ user / {id}' 、UserProfile);
  2. $ req- > assert( '| ^ \ d + $ |' )-> run();


しかし幸いなことに、PHPではクラスと関数に同じ名前を使用できるため、クラスのインスタンスを作成して返すラッパー関数を作成します。

Copy Source | Copy HTML



  1. 関数リクエスト( $メソッド$パス$コールバック
  2. {
  3. 新しいリクエストを返す$メソッド$パス$コールバック );
  4. }


これで、すべてをコンパクトかつ便利に書き直すことができます。

Copy Source | Copy HTML



  1. リクエスト( '/ user / {id}' 、UserProfile)-> assert( '| ^ \ d + $ |' )-> run();


2番目の不快な瞬間は、リクエストを保存して処理するセンターが1つもないことです。 なぜrun()関数を常に呼び出す必要があるのですか? ただし、このような単一のセンターを作成しても、新しいクエリ自体がキューに追加されると、はるかに簡単になります。 さらに、センターが2つあるべきではないため、シングルトンを実装する必要があります。 それで、私たちはすでにそれを書きます!

Copy Source | Copy HTML



  1. クラス申請
  2. {
  3. public $ requests = array ();
  4. /// ---
  5. //シングルトンを実装します
  6. 保護された静的 $インスタンス
  7. プライベート関数 __construct()
  8. {
  9. }
  10. プライベート関数 __clone()
  11. {
  12. }
  13. パブリックスタティック関数 getInstance()
  14. {
  15. if (!is_object(self :: $ instance ))
  16. {
  17. self :: $ instance = new self;
  18. }
  19. return self :: $インスタンス ;
  20. }
  21. public static function init()
  22. {
  23. self :: getInstance();
  24. }
  25. /// ---
  26. //すべてのリクエストを処理する内部関数
  27. プライベート関数 i_run()
  28. {
  29. foreach$ this- >要求as$ request
  30. {
  31. $ done = $ request- > run( $ params );
  32. if$ donetrueを返す場合 ;
  33. }
  34. falseを返します
  35. }
  36. // i_runの外部静的ラッパー関数
  37. //美学のためにのみ必要:Application :: run()はApplication :: getInstance()-> run()よりもきれいに見える
  38. public static function run()
  39. {
  40. return Application :: getInstance()-> i_run();
  41. }
  42. }


Requestクラスも少し変更します。

Copy Source | Copy HTML



  1. クラスリクエスト
  2. {
  3. <...>
  4. //クラスコンストラクター
  5. パブリック関数 __construct( $ method$ path$ callback
  6. {
  7. $ this-> method = strtolower( $ method );
  8. $ this- > path = $ path ;
  9. $ this-> callback = $ callback ;
  10. //このリクエストをアプリケーションのキューに追加します
  11. アプリケーション:: getInstance()->リクエスト[] = $ this ;
  12. }
  13. <...>
  14. パブリック関数 run()
  15. {
  16. <...>
  17. //すべてのチェックの後、コールバックを呼び出し、リクエスト変数を含む配列を渡します
  18. $ result = call_user_func_array( $ this-> callback、 $ this-> args);
  19. //コールバックがブール値を返した場合、それを返します
  20. if (is_bool( $ result ))
  21. return $ result ;
  22. //それ以外の場合、trueを返します
  23. 他に
  24. trueを返します
  25. }
  26. }


後者は、1つのパスを処理するために複数の要求を使用できるようにするために必要です。 コールバックがfalseを返す場合、次のリクエストが処理されます。そうでない場合、すべてが終了します。

一般に、ミニフレームワークの使用はかなりきれいでシンプルに見えます。

Copy Source | Copy HTML



  1. 新しいアプリケーション();
  2. リクエスト( '/ user / {id}' 、UserProfile)-> assert( '| ^ \ d + $ |' )-> run();
  3. アプリケーション::実行();


ただし、さらに簡素化できます。 PHP設定には、auto_prepend_fileとauto_append_fileの2つの優れたオプションがあり、メインスクリプトの実行前後に事前にスクリプト化されたPHPスクリプトを接続できます。 フレームワークを別のファイルに配置し、これらの関数を使用して添付できます。 最初の接続では、クラスを宣言してApplicationオブジェクトを作成し、2番目の接続ではApplication :: run()を呼び出します。 ApplicationクラスまたはRequestクラスが存在するかどうかを確認することにより、スクリプトが初めて実行されているかどうかを判断できます。

Copy Source | Copy HTML



  1. if (!class_exists( 'Application' ))
  2. {
  3. // Applicationクラスがまだ宣言されていない場合、スクリプトは初めて実行されます
  4. クラスリクエスト
  5. {
  6. <...>
  7. }
  8. 関数リクエスト( $メソッド$パス$コールバック
  9. {
  10. <...>
  11. }
  12. クラス申請
  13. {
  14. <...>
  15. }
  16. //アプリケーションの初期化、
  17. アプリケーション:: init();
  18. }
  19. 他に
  20. {
  21. //スクリプトは初めて実行されていません
  22. アプリケーション::実行();
  23. }


したがって、2つの「余分な」行を取り除きました。



これが、多かれ少なかれ機能的なミニフレームワークを書く方法です。 Silexには届きませんが、使用することは可能です。 完全なソースコード(わずかに変更)と使用例はhttp://fw.nizarium.com/にあり、同じミニフレームワークで動作します。

これを深刻なものと考えないでください。 これは単なる例であり、元々は私自身のために書かれたものです。 エラーがあれば、修正する準備ができています。



All Articles