原文發表在個人我的網站:利用 Composer 完善本身的 PHP 框架(一)——視圖裝載php
本教程示例代碼見 https://github.com/johnlui/My-First-Framework-based-on-Composerhtml
通過了上一個 系列教程 《利用 Composer 一步一步構建本身的 PHP 框架》,咱們組建了一個具備 路由
、MVC 架構
和 ORM
功能的基礎框架 MFFC
。接下來咱們繼續完善這個項目。前端
咱們先從目前用着 最不爽
的地方——視圖裝載下手。咱們將封裝一個視圖裝載類,讓它來幫咱們裝載視圖,並把變量傳遞進視圖。這個類將只暴露出來幾個簡單的接口,讓咱們在控制器裏面用的爽,讓咱們一邊寫代碼一邊笑。真的笑,笑出聲。:-Dlaravel
視圖裝載類要作的工做其實很簡單:git
本文中咱們將不會不引入模板引擎,只作裝載文件和傳遞變量的功能。github
咱們要引入視圖裝載器,這就正式打開了組件化的大門,因此咱們須要作一些準備工做。json
1. 啓動流程組件化bootstrap
將 public/index.php
裏面的代碼分離一部分到啓動器(bootstrap),新建 MFFC/bootstrap.php
文件:後端
<?php use Illuminate\Database\Capsule\Manager as Capsule; // 定義 BASE_PATH define('BASE_PATH', __DIR__); // Autoload 自動載入 require BASE_PATH.'/vendor/autoload.php'; // Eloquent ORM $capsule = new Capsule; $capsule->addConnection(require BASE_PATH.'/config/database.php'); $capsule->bootEloquent();
修改 public/index.php
爲:瀏覽器
<?php // 定義 PUBLIC_PATH define('PUBLIC_PATH', __DIR__); // 啓動器 require PUBLIC_PATH.'/../bootstrap.php'; // 路由配置、開始處理 require BASE_PATH.'/config/routes.php';
這時候咱們就完成了 入口文件 和 啓動器 的分離,並定義了兩個全局常量 BASE_PATH
和 PUBLIC_PATH
。
在這裏咱們須要特別注意一點:「引入路由配置文件」 這一步並不僅是簡單地引入了一個配置文件,路由文件的最後一行
Macaw::dispatch();
纔是真正執行某個控制器中某個 function
的地方,全部準備條件都應該在載入路由文件以前完成,例如 Eloquent 的初始化,還有之後咱們要使用的 Composer 包的初始化等等。
2. 引入錯誤頁面提示組件
咱們選擇 filp/whoops 做爲咱們錯誤提示組件包。
修改 composer.json
:
"require": { "codingbean/macaw": "dev-master", "illuminate/database": "*", "filp/whoops": "*" },
運行 composer update
,而後在 bootstrap.php
的最後添加:
// whoops 錯誤提示 $whoops = new \Whoops\Run; $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler); $whoops->register();
刷新 http://127.0.0.1:81 ,你獲得的應該仍是這個頁面:
下面咱們將增長路由配置中 無匹配項
的錯誤頁面,修改 config/routes.php
:
<?php use NoahBuscher\Macaw\Macaw; Macaw::get('', 'HomeController@home'); Macaw::get('fuck', function() { echo "成功!"; }); Macaw::$error_callback = function() { throw new Exception("路由無匹配項 404 Not Found"); }; Macaw::dispatch();
如今訪問一個隨意輸入的 URL,例如 http://127.0.0.1:81/asd ,咱們會看到如下畫面:
是否是有一種很熟悉的感受!
很不幸,這個錯誤提示包正是 Laravel 採用的那個,因此,咱們可愛的 MFFC
框架在長大之後仍是成了 Laravel
的樣子。%>_<%
完成基礎準備之後咱們正式開始製造視圖裝載器。
視圖裝載器是一個可插拔組件,咱們應該把全部可插拔組件所有歸到一處,在 MFFC 中建議放在 MFFC/services
下。
CI 框架提供的基礎組件庫叫 helpers
,Laravel 使用 illuminate/support
包提供一些可重用的系統函數。實際上 「illuminate/support」 這個包已經被咱們的 ORM 包 「illuminate/database」 依賴了,如今 MFFC 框架裏面已經能夠直接使用。,這個包的中文文檔見:http://laravel-china.org/docs/helpers
咱們並無像 CI 框架那樣把視圖裝載器放到系統核心,有如下兩個緣由:
下面開始着手實現視圖裝載器。
新建 MFFC/services
文件夾,並修改 composer.json
把這個文件夾下的全部類自動納入根命名空間:
"autoload": { "classmap": [ "app/controllers", "app/models", "services" ] }
新建 services/View.php
文件,內容以下:
<?php /** * \View */ class View { const VIEW_BASE_PATH = '/app/views/'; public $view; public $data; public function __construct($view) { $this->view = $view; } public static function make($viewName = null) { if ( ! $viewName ) { throw new InvalidArgumentException("視圖名稱不能爲空!"); } else { $viewFilePath = self::getFilePath($viewName); if ( is_file($viewFilePath) ) { return new View($viewFilePath); } else { throw new UnexpectedValueException("視圖文件不存在!"); } } } public function with($key, $value = null) { $this->data[$key] = $value; return $this; } private static function getFilePath($viewName) { $filePath = str_replace('.', '/', $viewName); return BASE_PATH.self::VIEW_BASE_PATH.$filePath.'.php'; } public function __call($method, $parameters) { if (starts_with($method, 'with')) { return $this->with(snake_case(substr($method, 4)), $parameters[0]); } throw new BadMethodCallException("方法 [$method] 不存在!."); } }
運行 composer dump-autoload
,完成之後,咱們就能夠在控制器中直接調用這個類了。
修改 controllers/HomeController.php
:
<?php /** * \HomeController */ class HomeController extends BaseController { public function home() { $this->view = View::make('home')->with('article',Article::first()) ->withTitle('MFFC :-D') ->withFuckMe('OK!'); } }
修改 controllers/BaseController.php
:
<?php /** * \BaseController */ class BaseController { protected $view; public function __construct() { } public function __destruct() { $view = $this->view; if ( $view instanceof View ) { extract($view->data); require $view->view; } } }
修改 app/views/home.php
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title><?php echo $title ?></title> </head> <body> <div class="article"> <h1><?php echo $article['title'] ?></h1> <div class="content"> <?php echo $article['content'] ?> </div> </div> <ul class="fuckme"> <li>Fuck Me !</li> <li> <?php echo $fuck_me ?> </li> </ul> </body> </html>
刷新,你將看到如下頁面:
至此,視圖裝載器實現完成。
下面我大體說一下設計視圖裝載器的基本思路:
make
,接受視圖名稱做爲參數,以 .
做爲目錄的間隔符。with('key', $value)
或者優雅的 withKey($value)
來給這個 View 對象插入要在視圖裏調用的變量。withFuckMe($value)
將採用蛇形命名法被轉化成 $fuck_me
供視圖使用。HomeController
的成員變量 $view
,這個變量是從 BaseController
中繼承得來。BaseController
中的析構函數 __destruct()
將在 function home()
執行完成後處理這個成員變量:extract
出視圖要用到的變量,require
視圖文件,將最終運算結果發送給瀏覽器,流程結束。