まえがき
このガイドでは、SPLライブラリ(標準PHPライブラリ)の機能を使用して、PHP 5.1で単純なMVCアーキテクチャ(Model-View-Controller、Model-Display-Controller)を構築する方法を学習します。
はじめに
PHPitでのPHP 5の最初の完全なチュートリアルへようこそ。 PHP 5の最新機能を利用するため、SPLライブラリがインストールされたPHP 5.1が必要です。
このチュートリアルでは、単純なMVCシステムの構築方法を示します(MVCアーキテクチャは、大規模なWebアプリケーションの最も一般的な設計パターンです)。 本格的なMVCシステムの作成の最初から最後までのすべての手順を説明します。
1つのエントリポイント
MVCで重要なことの1つは、多数のPHPファイルが次のようなことを行う代わりに、アプリケーションへの単一のエントリポイントです。
<?php
include( 'global.php' );
//ここにページコードがあります
?>
1つのファイルですべてのリクエストを処理します。 これは、新しいページを作成するたびにglobal.phpを接続することを心配する必要がないことを意味します。 この「1つのエントリポイント」はindex.phpと呼ばれ、現在は次のようになります。
<?php
//ここで何かをする
?>
ご覧のとおり、このスクリプトはまだ何もしていませんが、しばらくお待ちください。
すべてのリクエストをメインページに送信するには、mod_rewriteを使用し、.htaccessでRewriteRuleディレクティブを設定します。 次のコードを.htaccessファイルに貼り付け、index.phpと同じディレクトリに保存します。
Rewriteengine on
RewriteCond%{REQUEST_FILENAME}!-F
RewriteCond%{REQUEST_FILENAME}!-D
RewriteRule ^(。*)$ Index.php?Route = $ 1 [L、QSA]
まず、RewriteCondディレクティブを使用して、要求されたファイルが存在するかどうかを確認し、存在しない場合は、要求をindex.phpにリダイレクトします。 そうしないと、index.phpは画像のリクエストを含むサイトへのすべてのリクエストを処理しようとするため、ファイルの存在を確認する必要があります。 そして、私たちはそれを必要としません。
.htaccessまたはmod_rewriteを使用できない場合は、index.phpへのすべてのリクエストに手動で対処する必要があります。 言い換えると、すべてのリンクは「index.php?Route = [here-there is a request]」のように見える必要があります。 たとえば、「index.php?Route = chat / index」。
すべてのリクエストが1つのエントリポイントを通過したので、index.phpスクリプトの作成を開始できます。 最初に行う必要があるのは、システムの初期化です。 includeディレクトリとその中にstartup.phpファイルを作成しましょう(初期化ファイルが含まれます)。 次のコードをindex.phpに貼り付けます。
<?php
error_reporting ( E_ALL );
if( version_compare ( phpversion ()、 '5.1.0' 、 '<' )== true ){die( 'PHP5.1 Only' ); }
//定数:
定義 ( 'DIRSEP' 、 DIRECTORY_SEPARATOR );
//サイトファイルへのパスを見つける
$ site_path = realpath ( dirname ( __ FILE __ )。 DIRSEP 。 '..' 。 DIRSEP )。 DIRSEP ;
define ( 'site_path' 、 $ site_path );
この例では、定数を宣言し、システムファイルの場所を確認し、PHPのバージョン(少なくとも5.1)を確認します。
次に行うことは、グローバル値を格納するためのレジストリオブジェクト(ジャーナル、レジストリ)です。 変数を「グローバル」として指定したり、$ GLOBALS配列にアクセスしたりすることなく、システムの個々のオブジェクトに転送され、グローバル値にアクセスするために使用されます。 レジストリオブジェクトの詳細については、記事「PHPでグローバル値を使用する」を参照してください。
前の例に示したコードの後に、startup.phpファイルに次のコードを追加します。
$レジストリ =新しい レジストリ ;
今すぐシステムを起動しようとすると、次のエラーが表示される場合があります。
Fatal error: Class 'Registry' not found in g:\Projects\PHPit\content\simple mvc php5\demo\includes\startup.php on line 12
もちろん、レジストリクラス自体をまだ作成していないため、これは大きな驚きではありません。 クラスを持つファイルは、include()関数を使用して簡単に接続できます( 注:ところで、include()はそのような関数ではありませんが、言語の表現、 マナを見ると制御構造です )、しかしPHP 5の新機能の1つである__autoload()を使用します。
__autoload()マジック関数は、クラスを動的にロードするために使用されます。 PHPは、存在しないクラスを検出すると、最初に__autoload()関数を呼び出してから、エラーをスローします。 この機会に、その場でクラスをロードできます。
前の例のコードの前にこのコードを貼り付けます。
//クラスをその場でロード
関数 __autoload ( $ class_name ){
$ filename = strtolower ( $ class_name )。 '.php' ;
$ file = site_path 。 'classes' 。 DIRSEP $ファイル名 ;
if( file_exists ( $ file )== false ){
falseを 返し ます 。
}
インクルード( $ file );
}
__autoload()関数は、渡されたクラス名を引数として受け取り、同様の名前のファイルがクラスディレクトリに存在するかどうかを確認します。 ファイルがない場合、関数は単にfalseを返し、致命的なエラーがポップアップします。 ただし、ファイルが存在する場合はダウンロードされます。 つまり 必要なクラスが宣言され、エラーは発生しません。
Registryクラス自体はまだ作成されていないため、エラーが引き続き表示されます。 やってみましょう。
レジストリクラスの作成
Registryクラスは、個々のオブジェクト間でグローバル値を渡すために使用されます。 これは実際には、いくつかの小さなメソッドを実装する必要があるかなり単純なクラスです。
最初に、classesディレクトリとその中にregistry.phpファイルを作成します。 registry.phpに次のコードを貼り付けます。
<?php
クラス レジストリ {
private $ vars = array();
}
?>
これでRegistryクラスの「スケルトン」ができたので、それをメソッドでロードする必要があります。 値を設定するset()と値を取得するget()の2つのメソッドを記述します。 remove()メソッドを記述して値を削除することもできます。 これらのメソッドをレジストリクラスに追加します。
関数 セット ( $ key 、 $ var ){
if(isset( $ this- > vars [ $ key ])== true ){
新しい 例外を スロー ( 'varを設定できません `` 。 $ key 。 ' '。すでに設定されています。' );
}
$ this- > vars [ $ key ] = $ var ;
trueを 返し ます 。
}
function get ( $ key ){
if(isset( $ this- > vars [ $ key ])== false ){
nullを 返し ます 。
}
return $ this- > vars [ $ key ];
}
関数 remove ( $ var ){
設定解除( $ this- > vars [ $ key ]);
}
?>
これらのメソッドは単純で、クラスの属性である$ vars配列から要素をインストール、受信、削除します。 set()メソッドでは、指定されたキーを持つ値が既に存在するかどうかも確認し、存在する場合は例外をスローします。 これは、誤って値を上書きしないようにするためです。
これで完全なレジストリクラスが作成されましたが、そこで停止することはありません。 SPLライブラリの機能の1つであるArrayAccessを使用します。 SPL(Standard PHP Library、Standard PHP Libraryの略)は、一般的な問題を解決するために設計されたインターフェイスとクラスのコレクションです。 SPLインターフェースの1つであるArrayAccessを使用して、通常の配列のようにオブジェクトへのアクセスを提供できます。 例を見てみましょう:
<?php
$レジストリ =新しい レジストリ ;
//値を設定します
$ registry- > set ( 'name' 、 'Dennis Pallett' );
// get()を使用して値を取得します
echo $ registry- > get ( 'name' );
//アクセスを配列として使用して値を取得します
echo $レジストリ [ 'name' ]
?>
トリックは、$レジストリが配列のようになることですが、実際にはオブジェクトです。 もちろん、ArrayAccessには特別な利点はありませんが、毎回「-> get()」と書く必要がないため、コードの量を減らすことができます。 このインターフェイスを使用するには、クラスの最初の行(「クラスレジストリ」)を次のように修正する必要があります。
クラス レジストリはArrayAccessを実装します {
「Implements」というキーワードは、このインターフェイスクラスを実装していることをインタープリターに伝えます。これは、実際にArrayAccessです。
ArrayAccessインターフェイスを実装するクラスには、次のメソッドが必要です。
関数 offsetExists ( $ offset ){
return isset( $ this- > vars [ $ offset ]);
}
function offsetGet ( $ offset ){
return $ this- > get ( $ offset );
}
function offsetSet ( $ offset 、 $ value ){
$ this- > set ( $ offset 、 $ value );
}
function offsetUnset ( $ offset ){
設定解除( $ this- > vars [ $ offset ]);
}
これらの方法は一目瞭然です。 詳細については、 SPLのドキュメントを参照してください。
これで、ArrayAccessインターフェイスを実装したので、通常の配列としてオブジェクトにアクセスできます。 これは、前の例と次の両方で明確に示されています。
<?php
$レジストリ =新しい レジストリ ;
//値を設定します
$ registry [ 'name' ] = 'Dennis Pallett' ;
// get()を使用して値を取得します
echo $ registry- > get ( 'name' );
//アクセスを配列として使用して値を取得します
echo $レジストリ [ 'name' ]
?>
これでレジストリクラスが完成し、システムを起動しようとすると、すべてが機能するはずです(ただし、他には何も表示されません)。 初期化ファイルの設定が完了したら、MVCシステムを記述する次の手順に進むことができます。MVCアーキテクチャでは「モデル」と呼ばれるデータベースへのアクセスを実装します。
モデル
「M」またはモデルは、データベース(または他の外部ソース)にクエリを送信し、コントローラーに情報を提供するMVCシステムの一部です。 リクエストに応じて必要なモデルをロードすることは可能ですが、この場所でモデルとコントローラーの境界を少し消すことを好みます。 コントローラーは、個別のモデルではなく、データベースとの対話ライブラリを介してデータベースと直接連携します。 別の方法でやりたいかもしれませんが、それは好みの問題です。
データベースへの接続を確立するために必要なコードを記述し、index.phpに配置する必要があります。 データベースを操作するための優れたライブラリ(私自身のAutoCRUDを含む)は数多くありますが、PHP 5にはすでにそのようなライブラリ(PDO)があります。 したがって、他のものを使用する必要はありません。
次のコードをindex.phpファイルに貼り付けます(初期化ファイルを接続した後):
#データベースに接続する
$ db = new PDO ( 'mysql:host = localhost; dbname = demo' 、 '[user]' 、 '[password]' );
$ registry- > set ( 'db' 、 $ db );
この例では、最初にPDOライブラリの新しいインスタンスを作成し、MySQLデータベースに接続します。 次に、レジストリクラスを使用して$ db変数にグローバルにアクセスできるようにします。
システムのモデルコンポーネントは準備ができているので、コントローラの作成に移りましょう。
コントローラーを記述することは、要求に応じて目的のコントローラーをロードするRouterクラスを記述することも意味します($ルート変数はURLを介してindex.phpに渡されることに注意してください)。
ルータークラス
Routerクラスはリクエストを解析し、必要なコントローラーをロードします。 「スケルトン」クラスを作成します。
<?php
クラス ルーター {
プライベート$レジストリ ;
プライベート$パス ;
private $ args = array();
関数 __construct ( $レジストリ ){
$ this- > registry = $ registry ;
}
}
?>
次に、index.phpに次の行を追加します。
#ルーターをダウンロード
$ router = new Router ( $レジストリ );
$ registry- > set ( 'router' 、 $ router );
RouterクラスをMVCシステムに追加しましたが、まだ何もしませんので、作業に必要なメソッドを追加しましょう。
最初に記述するのは、すべてのコントローラーが置かれるディレクトリを設定するsetPath()メソッドです。 メソッドは次のとおりであり、Routerクラスに追加する必要があります。
関数 setPath ( $ path ){
$ path = trim ( $ path 、 '/ \\' );
$パス 。= DIRSEP ;
if( is_dir ( $ path )== false ){
新しい 例外を スロー ( '無効なコントローラーパス: ``' 。 $ path 。 '' ' );
}
$ this- > path = $ path ;
}
次に、index.phpに次の行を追加します。
$ router- > setPath ( site_path 。 'controllers' );
コントローラーへのパスを設定したので、コントローラーをロードするメソッド自体を記述します。 このメソッドはデリゲート()と呼ばれ、リクエストを解析します。 このメソッドの最初の部分は次のとおりです。
関数 デリゲート (){
//パスを分析します
$ this- > getController ( $ file 、 $ controller 、 $ action 、 $ args );
ご覧のように、別のメソッドgetController()を使用して、コントローラーの名前と他のいくつかの変数を取得します。 このメソッドは次のようになります。
プライベート 関数 getController (& $ file 、& $ controller 、& $ action 、& $ args ){
$ route =(empty( $ _GET [ 'route' ]))? '' : $ _GET [ 'ルート' ];
if(empty( $ route )){ $ route = 'index' ; }
//分割されたパーツを取得します
$ route = trim ( $ route 、 '/ \\' );
$ parts = explode ( '/' 、 $ route );
//適切なコントローラーを見つける
$ cmd_path = $ this- > path ;
foreach( $ parts as $ part ){
$ fullpath = $ cmd_path 。 $ part ;
//このパスを持つフォルダーはありますか?
if( is_dir ( $ fullpath )){
$ cmd_path 。= $ part 。 DIRSEP ;
array_shift ( $ parts );
続ける;
}
//ファイルを見つけます
if( is_file ( $ fullpath 。 '.php' )){
$コントローラー = $ part ;
array_shift ( $ parts );
休憩;
}
}
if(empty( $ controller )){ $ controller = 'index' ; };
//アクションを取得します
$ action = array_shift ( $ parts );
if(empty( $ action )){ $ action = 'index' ; }
$ file = $ cmd_path 。 $コントローラー 。 '.php' ;
$ args = $ parts ;
}
この方法を見ていきましょう。 最初に、リクエストから$ルート変数の値を取得し、次にexplode()関数を使用してそれを部分に分割します。 たとえば、クエリ「members / view」は、array( 'members'、 'view')のような配列に変換されます。
次に、foreachループを使用して、各部分を調べ、この部分がディレクトリかどうかを確認します。 そうである場合、彼はそれをファイルへのパスに割り当て、次の部分をチェックします。 これにより、コントローラをサブディレクトリに配置し、コントローラの階層を取得できます。 要求の現在の部分がディレクトリではなくファイルである場合、それは$コントローラー変数に保存され、必要なコントローラーが見つかったためループを終了します。
ループの後、コントローラーの名前で変数をチェックします。 空の場合、デフォルトのコントローラーとなる「インデックス」コントローラーを使用します。 次に、メソッドは実行するアクションを決定します。 コントローラーは、いくつかのメソッドで構成されるクラスです。 アクションは特定のメソッドを示します。 アクションが指定されていない場合、デフォルトのアクションである「インデックス」を使用します。
そして最後に、3つの変数を組み合わせたコントローラーファイルへのフルパスを取得します:パス、コントローラーの名前、および拡張子 "php"。
リクエストを分析したので、デリゲート()メソッドを呼び出してコントローラーをロードし、アクションを完了します。 完全なデリゲート()メソッドは次のようになります。
関数 デリゲート (){
//パスを分析します
$ this- > getController ( $ file 、 $ controller 、 $ action 、 $ args );
//ファイルは利用可能ですか?
if( is_readable ( $ file )== false ){
die( '404 Not Found' );
}
//ファイルを添付します
インクルード( $ file );
//コントローラーのインスタンスを作成します
$ class = 'Controller_' 。 $コントローラー ;
$ controller = new $ class ( $ this- > registry );
//アクションは利用可能ですか?
if( is_callable (array( $ controller 、 $ action ))== false ){
die( '404 Not Found' );
}
//アクションを実行します
$コントローラー -> $ action ();
}
getController()メソッドを使用してリクエストを分析した後、ファイルが実際に存在するかどうかを確認し、存在しない場合は単純なエラーメッセージを返します。
その後、ファイルをコントローラーに接続し、そのクラスのインスタンスを作成します。このインスタンスは「Controller_ [name]」と呼ばれます。 少し後で、コントローラーについて詳しく説明します。
次に、指定されたアクション(メソッドなど)が存在するかどうか、およびアクセスできるかどうかを確認します(これにはis_callable()関数を使用します)。 最後に、アクション自体を直接実行し、Routerクラスの役割が終了します。
デリゲート()メソッドを完全に記述したら、次の行をindex.phpファイルに追加します。
$ルーター -> デリゲート ();
ここでシステムを起動しようとすると、次のエラーが表示されます(もちろん、コントローラーディレクトリがまだない場合)。
Fatal error: Uncaught exception 'Exception' with message 'Invalid controller path: `g:\Projects\PHPit\content\simple mvc php5\demo\controllers\`' in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php:18 Stack trace: #0 g:\Projects\PHPit\content\simple mvc php5\demo\index.php(13): Router->setPath('g:\Projects\PHP...') #1 {main} thrown in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php on line 18
または、まだコントローラがないため、「404 Not Found」エラーが表示されます。 しかし、これは私たちが今やることです。
コントローラー
MVCシステムのコントローラーは非常にシンプルで、時間もほとんどかかりません。 最初に、controllersディレクトリが存在することを確認してください。 classesディレクトリにcontroller_base.phpファイルを作成し、次のコードをそこに貼り付けます。
<?php
抽象 クラス Controller_Base {
保護された$レジストリ 。
関数 __construct ( $レジストリ ){
$ this- > registry = $ registry ;
}
抽象 関数 インデックス ();
}
?>
この抽象クラスは、すべてのコントローラーの親クラスになります。 レジストリクラスのローカルコピーを保存し、abstract index()メソッドを使用して、すべての子コントローラーにこのメソッドを強制的に実装します。
最初のコントローラーを作成します。 controllersディレクトリにindex.phpファイルを作成し、次のコードをそこに貼り付けます。
<?php
クラス Controller_IndexはController_Baseを 拡張し ます {
関数 インデックス (){
echo 'こんにちは、私のMVCシステムから' ;
}
}
?>
最初のコントローラーを作成したばかりで、システムを起動しようとすると、次のように表示されます。
(フルサイズ、1024x357、115 KB)
これは、ルータークラスが作業を完了し、必要なコントローラーから必要なアクションを開始したことを意味します。 リクエスト「members / view」に対応する別のコントローラーを作成しましょう。 コントローラーディレクトリにmembers.phpファイルを作成し、次のコードをそこに貼り付けます。
<?php
クラス Controller_MembersはController_Baseを 拡張し ます {
関数 インデックス (){
echo '`members`コントローラのデフォルトのインデックス' ;
}
関数 ビュー (){
echo 'あなたはメンバー/ビューリクエストを表示しています' ;
}
}
?>
次に、「members / view」または「index.php?Route = members / view」のリクエストでMVCシステムに入ります。 次の結果が表示されます。
(フルサイズ、1024x320、117 KB)
新しいコントローラーを作成してメソッドを追加するだけで、新しいページを作成することができ、システム自体を変更する必要はありませんでした。 さらに、コントローラーはglobal.phpファイルを含める必要も、そのようなことをする必要もありません。
コントローラーができたので、残っているのは「V」または「View」(「Display」)だけです。
ディスプレイ
モデルと同様に、MVCシステムでViewコンポーネントを作成するためのいくつかの異なるオプションがあります。 Routerクラスに、「view_ {name} .php」のような名前の別のファイルを自動的にロードするように教えることができます。 しかし、ガイドをより理解しやすくするために、テンプレートの出力を処理するTemplateクラスを作成します。
最初に、classesディレクトリにtemplate.phpファイルを作成し、次のコードをそこに貼り付けます。
<?php
クラス テンプレート {
プライベート$レジストリ ;
private $ vars = array();
関数 __construct ( $レジストリ ){
$ this- > registry = $ registry ;
}
}
?>
これで、Templateクラスの基本構造ができました。 次のステップは、Routerクラスに関連付けられた行の直前に、このコードをindex.phpファイルに追加することです。
#テンプレートオブジェクトを作成する
$ template = new Template ( $ registry );
$ registry- > set ( 'template' 、 $ template );
モデルとコントローラーの値を使用する必要があるため、テンプレートで使用可能な変数を設定するset()メソッドを記述します。 例を見てみましょう:
関数 セット ( $ varname 、 $ value 、 $ overwrite = false ){
if(isset( $ this- > vars [ $ varname ])== true AND $ overwrite == false ){
trigger_error ( 'varを設定できません `` 。 $ varname 。 ' '。すでに設定されているため、上書きは許可されていません。' 、 E_USER_NOTICE );
falseを 返し ます 。
}
$ this- > vars [ $ varname ] = $ value ;
trueを 返し ます 。
}
関数 remove ( $ varname ){
設定解除( $ this- > vars [ $ varname ]);
trueを 返し ます 。
}
set()およびremove()メソッドは非常にシンプルで、それぞれ変数の設定と削除に使用されます。
テンプレートを表示するshow()メソッドを書きましょう。 最も簡単な方法は、すべてのテンプレートファイルを保存する別のテンプレートディレクトリを作成し、include()を使用してテンプレートを表示することです。 もちろん、独自のshow()メソッドは完全に異なる場合があり、データベースからテンプレートをロードするか、他のことを行います。 クソを見てみましょう