構建本身的PHP框架(視圖裝載)

完整項目地址:https://github.com/Evai/Aier

 

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

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

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

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

基礎準備

咱們要引入視圖裝載器,這就正式打開了組件化的大門,因此咱們須要作一些準備工做。github

啓動流程組件化

將  public/index.php  裏面的代碼分離一部分到啓動器(bootstrap),新建  MFFC/bootstrap.php  文件:json

<?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  爲:bootstrap

 
 <?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 做爲咱們錯誤提示組件包。app

修改  composer.json :

"require": {

  "codingbean/macaw": "dev-master",

  "illuminate/database": "*",

  "filp/whoops": "*"

}

 

運行  composer update ,而後將  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();

// whoops 錯誤提示

$whoops = new \Whoops\Run;

$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);

$whoops->register();

 

下面咱們將增長路由配置中  無匹配項  的錯誤頁面,修改  config/routes.php :

<?php

use NoahBuscher\Macaw\Macaw as Route;

Route::get('/', function() {
  echo "Welcome";
});

Route::get('/name/(:all)', function($name) {
  echo 'Your name is '.$name;
});

Route::get('home', 'HomeController@home');


Route::error(function() {
    throw new Exception("404 Not Found");
});

Route::dispatch();

 

如今訪問一個隨意輸入的 URL ,咱們會看到如下畫面:

 

是否是有一種很高大上的感受!(連報錯都這麼優雅⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄)

實現裝載器

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

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

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

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

 

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

新建  MFFC/services  文件夾,並修改  composer.json  把這個文件夾下的全部類自動納入根命名空間:

{
  "require": {
    "noahbuscher/macaw": "dev-master",
    "illuminate/database": "*",
    "filp/whoops": "*"
  },
  "autoload": {
    "classmap": [
      "app/controllers",
      "app/models",
      "app/class",
      "services"
    ]
  }
}

 

新建  services/View.php  文件,內容以下:

<?php
/**
 * ViewLoad
 */
class View
{
    const VIEW_BASE_PATH = '/app/views/';

    public $view;
    public $data;

    /**
     * View constructor.
     * @param $view
     */
    public function __construct($view)
    {
        $this->view = $view;
    }

    /**
     * 建立視圖
     * @param null $viewName
     * @return 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("視圖文件不存在!");
            }

        }
    }

    /**
     * 變量傳遞
     * @param $key
     * @param null $value
     * @return $this
     */
    public function with($key, $value = null)
    {
        $this->data[$key] = $value;
        return $this;
    }

    /**
     * 視圖路徑
     * @param $viewName
     * @return string
     */
    private static function getFilePath($viewName)
    {
        $filePath = str_replace('.', '/', $viewName);
        return BASE_PATH.self::VIEW_BASE_PATH.$filePath.'.php';
    }

    /**
     * @param $method
     * @param $parameters
     * @return View
     */
    public function __call($method, $parameters)
    {
        if (starts_with($method, 'with'))
        {
            return $this->with(snake_case(substr($method, 4)), $parameters[0]);
        }

        throw new BadMethodCallException("方法 [$method] 不存在!.");
    }

    /**
     * 傳輸視圖及變量
     */
    public function __destruct()
    {
        if ($this->data) extract($this->data);

        require $this->view;
    }
}

 

運行  composer dump-autoload ,完成之後,咱們就能夠在控制器中直接調用這個類了。

修改  controllers/HomeController.php :

<?php

class HomeController extends BaseController
{

    public function home()
    {
        return View::make('home')
            ->with('article', Articles::find(1))
            ->withTitle('Frame')
            ->withShowMsg('hello world');
    }

}
 

修改  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['name'] ?></h1>

    <div class="content">

        <?php echo $article  ?>

    </div>

</div>

<ul class="msg">

    <h1><?php echo $show_msg ?></h1>

</ul>

</body>

</html>

 

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

 

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


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

  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  視圖文件,將最終運算結果發送給瀏覽器,流程結束。
相關文章
相關標籤/搜索