利用 Composer 完善本身的 PHP 框架(一)——視圖裝載

利用 Composer 完善本身的 PHP 框架(一)——視圖裝載

本教程示例代碼見 https://github.com/johnlui/My-First-Framework-based-on-Composerphp

回顧

通過了上一個 系列教程  《利用 Composer 一步一步構建本身的 PHP 框架》 ,咱們組建了一個具備  路由 、 MVC 架構  和  ORM  功能的基礎框架  MFFC 。接下來咱們繼續完善這個項目。html

咱們先從目前用着  最不爽  的地方——視圖裝載下手。咱們將封裝一個視圖裝載類,讓它來幫咱們裝載視圖,並把變量傳遞進視圖。這個類將只暴露出來幾個簡單的接口,讓咱們在控制器裏面用的爽,讓咱們一邊寫代碼一邊笑。真的笑,笑出聲。:-D前端

正文

構思

視圖裝載類要作的工做其實很簡單:laravel

1. 根據視圖名稱找到視圖文件,支持文件夾git

2. 更加方便,更加優雅地把變量的值傳遞進視圖github

本文中咱們將不會不引入模板引擎,只作裝載文件和傳遞變量的功能。json

基礎準備

咱們要引入視圖裝載器,這就正式打開了組件化的大門,因此咱們須要作一些準備工做。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 包的初始化等等。

引入錯誤頁面提示組件

咱們選擇 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 ,你獲得的應該仍是這個頁面:

Image

下面咱們將增長路由配置中  無匹配項  的錯誤頁面,修改  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 ,咱們會看到如下畫面:

Image

是否是有一種很熟悉的感受!

很不幸,這個錯誤提示包正是 Laravel 採用的那個,因此,咱們可愛的  MFFC  框架在長大之後仍是成了  Laravel  的樣子。%>_<%

實現裝載器

完成基礎準備之後咱們正式開始製造視圖裝載器。

視圖裝載器是一個可插拔組件,咱們應該把全部可插拔組件所有歸到一處,在 MFFC 中建議放在  MFFC/services  下。

CI 框架提供的基礎組件庫叫  helpers ,Laravel 使用  illuminate/support  包提供一些可重用的系統函數。實際上 「illuminate/support」 這個包已經被咱們的 ORM 包 「illuminate/database」 依賴了,如今 MFFC 框架裏面已經能夠直接使用。,這個包的中文文檔見:http://laravel-china.org/docs/helpers

咱們並無像 CI 框架那樣把視圖裝載器放到系統核心,有如下兩個緣由:

  1. 基於命名空間與自動加載的調用方式更加節省資源
  2. 在移動互聯網和大前端愈演愈烈的時代,後端愈來愈 API 化、 json 化。不少時候都不到視圖,沒有必要再增長無畏的消耗。

 

下面開始着手實現視圖裝載器。

新建  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>

   

刷新,你將看到如下頁面:

Image

至此,視圖裝載器實現完成。

下面我大體說一下設計視圖裝載器的基本思路:

  1. 這個視圖裝載器類模仿了 Laravel 的 View 類,它實現了一個靜態方法  make ,接受視圖名稱做爲參數,以  .  做爲目錄的間隔符。
  2. make 靜態方法會檢查視圖名稱是否爲空,檢查視圖文件是否存在,並給出相應的異常。這就是咱們引入異常處理包的緣由。
  3. 視圖名稱合法且文件存在時,實例化一個 View 類的對象,返回。
  4. 使用  with('key', $value)  或者優雅的  withKey($value)  來給這個 View 對象插入要在視圖裏調用的變量。 withFuckMe($value)  將採用蛇形命名法被轉化成  $fuck_me  供視圖使用。
  5. 最終組裝好的 View 對象會被賦給  HomeController  的成員變量  $view ,這個變量是從  BaseController  中繼承得來。
  6. 父類  BaseController  中的析構函數  __destruct()  將在  function home()  執行完成後處理這個成員變量: extract  出視圖要用到的變量, require  視圖文件,將最終運算結果發送給瀏覽器,流程結束。
相關文章
相關標籤/搜索