独自の PHP MVC フレームワークを開発する (1)
このチュートリアルは、John Squibb の『Build a PHP MVC Framework in an Hour』から翻訳されたものですが、元のアドレス: http://johnsquibb.com/tutorials
このチュートリアルでは、誰もが MVC モードを使用した PHP アプリケーション開発の基本概念を習得できます。このチュートリアルは 3 つの部分に分かれており、これは最初の部分です。現在、誰もが使用できる人気のフレームワークが数多く市場に出ていますが、mvc モデルを使用すると、アプリケーションの開発に費やす時間を大幅に削減でき、プロジェクトのソース コードをより適切に整理できます。 、その中には、他のプロジェクトでも使用できるモジュールもあります。ここで、簡単な MVC フレームワークの書き方を教えたいと思います。このプロジェクトは非常にシンプルで軽量であるため、ベストプラクティスではない可能性があり、安全でもありません。実際のアプリケーションでは改善する必要があります。
使用されるテクノロジー: PHP、オブジェクト指向開発手法。
開始
まず、Web サイトのルート ディレクトリに 3 つのフォルダーを作成します
次に、ルート ディレクトリに新しいファイルを作成します:
プロジェクト構造は次のようになります
§ Web サイトのルート ディレクトリ
§index.php
§ モデル/
§ ビュー/
§ コントローラー/
<?php //应用的根目录就是index.php的父目录 define("SERVER_ROOT", dirname(__FILE__)); //你的域名.comm 是你的服务器域名 define('SITE_ROOT' , 'http://你的域名.com');
任意の PHP ファイルで Web サイトのルート ディレクトリを定義すると、他のディレクトリにある PHP ファイルを簡単に参照できます。これは、index.php がエントリ ファイルであるため、アプリケーション全体でその中で定義された PHP ファイルにアクセスできるためです。変数。
ルーターの router.php を設定します (ユーザーリクエストを対応するコントローラーに転送します)
route.php のコード:
<?php //获取所有请求 $request = $_SERVER['QUERY_STRING'];
次に、index.php に Route.php を導入する必要があります。
//解析$request变量,得到用户请求的页面(page1)和其它GET变量(&分隔的变量)如一个请求http://你的域名.com/index.php?page1&article=buildawebsite,则被解析为array("page1", "article=buildawebsite") $parsed = explode('&' , $request); //用户请求的页面,如上面的page1,为$parsed第一个变量,shift之后,数组为array("article=buildawebsite") $page = array_shift($parsed); //剩下的为GET变量,把它们解析出来 $getVars = array(); foreach ($parsed as $argument) { //用"="分隔字符串,左边为变量,右边为值 list($variable , $value) = split('=' , $argument); $getVars[$variable] = $value; } //这是测试语句,一会儿会删除 print "The page your requested is '$page'"; print '<br/>'; $vars = print_r($getVars, TRUE); print "The following GET vars were passed to the page:<pre class="brush:php;toolbar:false">".$vars."";
<?php /** * 定义文档路径 */ define("SERVER_ROOT", dirname(__FILE__)); define('SITE_ROOT' , 'http://你的域名.com'); /** * 引入router.php */ require_once(SERVER_ROOT . '/controllers/' . 'router.php'); ?>
http://yourdomain.com/index.php?news&article=howtobuildaframework
上記の出力がない場合は、サーバー構成が正しいかどうかを確認し、コードにエラーがないかどうかを確認してください。
The page you requested is 'news' The following GET vars were passed to the page: Array ( [article] => howtobuildaframework )
コントローラーフォルダーに「news.php」という名前の新しいファイルを作成し、次のクラスを定義します:
<?php /** * 这个文件处理文章的查询,并提供文章 */ class News_Controller { /** * $template变量会保存与此控制器相关的"view(视图)"的文件名,不包括.php后缀 */ public $template = 'news'; /** * 此方法为route.php默认调用 * * @param array $getVars 传入到index.php的GET变量数组 */ public function main(array $getVars) { //测试代码,以后会删除 print "We are in news!"; print '<br/>'; $vars = print_r($getVars, TRUE); print ( "The following GET vars were passed to this controller:" . "<pre class="brush:php;toolbar:false">".$vars."" ); } }
<?php /** * 此文件会把所有的传入参数分派到相应的控制器中 */ //获取请求参数 $request = $_SERVER['QUERY_STRING']; //解析请求页面和其它GET变量 $parsed = explode('&' , $request); //页面是第一个元素 $page = array_shift($parsed); //剩余的为GET变量,也把它们解析出来 $getVars = array(); foreach ($parsed as $argument) { //split GET vars along '=' symbol to separate variable, values list($variable , $value) = split('=' , $argument); $getVars[$variable] = $value; } //构成控制器文件路径 $target = SERVER_ROOT . '/controllers/' . $page . '.php'; //引入目标文件 if (file_exists($target)) { include_once($target); //修改page变量,以符合命名规范(如$page="news",我们的约定是首字母大写,控制器的话就在后面加上“<strong>_Controller”</strong>,即News_Controller) $class = ucfirst($page) . '_Controller'; //初始化对应的类 if (class_exists($class)) { $controller = new $class; } else { //类的命名正确吗? die('class does not exist!'); } } else { //不能在controllers找到此文件 die('page does not exist!'); } //一但初始化了控制器,就调用它的默认函数main(); //把get变量传给它 $controller->main($getVars);?>
<?php /** * 新闻模型为新闻控制器做复杂的后台操作 */ class News_Model { public function __construct() { print 'I am the news model'; } }
现在,我们需要对新闻控制器稍做一些更改,打开controllers里的news.php,把News_Controller类的main函数的代码改为如下,这样,我们就会在“News_Model”初始化时,看到打印在屏幕上的信息:
public function main(array $getVars) { $newsModel = new News_Model; }
Fatal error: Class 'News_Model' not found in /var/www/mvc/controllers/news.php on line xx
//当类初始化时,自动引入相关文件 function __autoload($className) { //解析文件名,得到文件的存放路径,如News_Model表示存放在models文件夹里的news.php(这里是作者的命名约定) list($filename , $suffix) = split('_' , $className); //构成文件路径 $file = SERVER_ROOT . '/models/' . strtolower($filename) . '.php'; //获取文件 if (file_exists($file)) { //引入文件 include_once($file); } else { //文件不存在 die("File '$filename' containing class '$className' not found."); } }
保存route.php,再刷新一次浏览器,你会看到:
I am the news model
让我们在新闻模型类里定义一些函数来提供文章。现在,我们只简单的定义了一个数组,并保存一些文章,然后提供一个函数,让控制器从中根据标题获取一篇文章。修改models/news.php:
<?php /** * 新闻模型为新闻控制器做复杂的后台操作 * */ class News_Model { /** * 文章数组. key为文章标题, 值为相应的 * 文章。 */ private $articles = array ( //文章1 'new' => array ( 'title' => 'New Website' , 'content' => 'Welcome to the site! We are glad to have you here.' ) , //2 'mvc' => array ( 'title' => 'PHP MVC Frameworks are Awesome!' , 'content' => 'It really is very easy. Take it from us!' ) , //3 'test' => array ( 'title' => 'Testing' , 'content' => 'This is just a measly test article.' ) ); public function __construct() { } /** * 根据标题获取文章 * * @param string $articleName * * @return array $article */ public function get_article($articleName) { //从数组中获取文章 $article = $this->articles[$articleName]; return $article; } }?>
public function main(array $getVars) { $newsModel = new News_Model; //获取一篇文章 $article = $newsModel->get_article('test'); print_r($article); }
如果访问如下网址:
§ http://yourdomain.com/mvc/index.php?news&article=test
你会看到如下输出:
Array ( [title] => Testing [content] => This is just a measly test article. )
现在我们已经有控制器和模型了,只差一个视图。视图是表现层,它是你的应用中,与用户接触最频繁的部分。之前我提到过,视图是提供与业务逻辑分离的用户接口,有很多方法可以做到这个。你可以使用模板引擎Smarty或其它类似的。你也可以写一个自己的模板引擎,但那肯定是相当艰巨的任务。最后,你可以使用原生php视图。
对于目前来说,php视图足够了。这个就像以前php与html代码混合编程一样,但是有一点不同是,我们的业务逻辑已经和视图分离了。看一下如下代码:
<html> <head></head> <body> <h1>Welcome to Our Website!</h1> <hr/> <h2>News</h2> <h4><?=$data['title'];?></h4> <p><?=$data['content'];?></p> </body> </html>
注意,嵌入的php标签利用了PHP 快捷操作符。这样就能够把我们的内容直接输出到HTML里面了。在views文件夹里新建一个文件“news.php”,把上述代码拷贝进来。现在我们有了视图文件,但是我们需要一个与视图交互的方法。在models文件夹里新建一个文件“view.php”,添加如下代码:
<?php /** * 在我们的MVC框架中,处理视图的功能 */ class View_Model { /** * 保存赋给视图模板的变量 */ private $data = array(); /** * 保存视图渲染状态 */ private $render = FALSE; /** * 加载一个视图模板 */ public function __construct($template) { //构成完整文件路径 $file = SERVER_ROOT . '/views/' . strtolower($template) . '.php'; if (file_exists($file)) { /** * 当模型对象销毁时才能渲染视图 * 如果现在就渲染视图,那么我们就不能给视图模板赋予变量 * 所以此处先保存要渲染的视图文件路径 */ $this->render = $file; } } /** * 接受从控制器赋予的变量,并保存在data数组中 * * @param $variable * @param $value */ public function assign($variable , $value) { $this->data[$variable] = $value; } public function __destruct() { //把类中的data数组变为该函数的局部变量,以方便在视图模板中使用 $data = $this->data; //渲染视图 include($this->render); } }
<?php /* *这个文件处理文章的查询,并产生新闻文章* */ class News_Controller{ /** * $template变量会保存与此控制器相关的"view(视图)"的文件名,不包括.php后缀 * */ public $template = 'news'; /** * 此方法为route.php默认调用 * * @param array $getVars 传入到index.php的GET变量数组 */ public function main(array $getVars) { $newsModel = new News_Model; //获取一片文章 $article = $newsModel->get_article($getVars['article']); //创建一个视图,并传入该控制器的template变量 $view = new View_Model($this->template); //把文章数据赋给视图模板 $view->assign('title' , $article['title']); $view->assign('content' , $article['content']); } } ?>