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

视图装载类要做的工作其实很简单:

1. 根据视图名称找到视图文件,支持文件夹

2. 更加方便,更加优雅地把变量的值传递进视图

本文中我们将不会不引入模板引擎,只做装载文件和传递变量的功能。

基础准备

我们要引入视图装载器,这就正式打开了组件化的大门,所以我们需要做一些准备工作。

启动流程组件化

将  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  修改为如下:

<?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  视图文件,将最终运算结果发送给浏览器,流程结束。

构建自己的PHP框架(视图装载)的更多相关文章

  1. 利用 Composer 完善自己的 PHP 框架(一)——视图装载

    本教程示例代码见 https://github.com/johnlui/My-First-Framework-based-on-Composer 回顾 经过了上一个 系列教程  <利用 Comp ...

  2. 如何构建Android MVVM 应用框架

    概述 说到Android MVVM,相信大家都会想到Google 2015年推出的DataBinding框架.然而两者的概念是不一样的,不能混为一谈.MVVM是一种架构模式,而DataBinding是 ...

  3. net 和Mono 构建的HTTP服务框架

    Nancy是一个基于.net 和Mono 构建的HTTP服务框架,是一个非常轻量级的web框架. 设计用于处理 DELETE, GET, HEAD, OPTIONS, POST, PUT 和 PATC ...

  4. 利用 Composer 一步一步构建自己的 PHP 框架(二)——构建路由

    本教程示例代码见 https://github.com/johnlui/My-First-Framework-based-on-Composer 上一篇中我们已经建立了一个空的 Composer 项目 ...

  5. ThinkPHP框架视图详细介绍 View 视图--模板(九)

    原文:ThinkPHP框架视图详细介绍 View 视图--模板(九) 视图也是ThinkPHP使用的核心部分: 一.模板的使用 a.规则 模板文件夹下[TPL]/[分组文件夹/][模板主题文件夹/]和 ...

  6. 构建Java并发模型框架

    Java的多线程特性为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦.线程间同步.数据一致性等烦琐的问题需要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误.另外,应用逻辑和线程逻辑纠 ...

  7. 第5章——使用 Razor(MVC框架视图引擎)

    Razor 是MVC框架视图引擎的名称. 本章提供 Razor 语法的快速教程,以使你能够识别 Razor 表达式. 本章不打算提供 Razor 的完整参考,而将其视为一个语法速成教程.在本书的后续内 ...

  8. CI框架视图继承

    CI(CodeIgniter)框架 视图继承 这个代码不是我撸的 ... 当时在哪儿找的忘了 ... 如果有侵权什么的 ... 联系我删了 ... 需要去core里面创建一个MY_loader.php ...

  9. 手把手0基础项目实战(一)——教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)...

    原文:手把手0基础项目实战(一)--教你搭建一套可自动化构建的微服务框架(SpringBoot+Dubbo+Docker+Jenkins)... 本文你将学到什么? 本文将以原理+实战的方式,首先对& ...

随机推荐

  1. angular内置指令相关知识

    原文地址 https://www.jianshu.com/p/5a5b43a8e91f 大纲 1.angular指令的分类 2.angular指令之——组件 3.angular指令之——属性指令 (n ...

  2. HTTP协议和HTTPS协议初探

    概况 HTTP是hypertext transfer protocol(超文本传输协议)的简写.它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEBserver之间交换数据的过程. HT ...

  3. Android shape画圆点

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  4. springMVC注解@initbinder

    在实际操作中经常会碰到表单中的日期 字符串和Javabean中的日期类型的属性自动转换, 而springMVC默认不支持这个格式的转换,所以必须要手动配置, 自定义数据类型的绑定才能实现这个功能. 比 ...

  5. Android中的动画详解系列【3】——自定义动画研究

    在上一篇中我们使用到了位移动画TranslateAnimation,下面我们先来看看TranslateAnimation是如何实现Animation中的抽象方法的: /* * Copyright (C ...

  6. linux网络编程实现投票功能

    投票系统 1.说明: 写了一个投票系统.过程是先配置好server.在写一个网上投票功能,要实现网上投票功能. 事实上功能实现还是非常easy的,麻烦一点的在于过程比較繁杂,要做的东西还是挺多的! 2 ...

  7. 剔除list中相同的结构体数据

    剔除list中相同的结构体数据,有三个思路:1.两层循环,逐个比较 2.使用set容器来剔除 3.使用unique方法去重 // deduplication.cpp : 定义控制台应用程序的入口点. ...

  8. js进阶 11-6 jquery如何获取和设置元素的宽高(jquery多方法)

    js进阶 11-6  jquery如何获取和设置元素的宽高(jquery多方法) 一.总结 一句话总结:jquery里面多是方法啊,比如jquery对象的宽高.所以取值是方法,赋值就是方法里面带参数. ...

  9. Android 设置alpha值来制作透明与渐变效果的实例

    Android系统支持的颜色是由4个值组成的,前3个为RGB,也就是我们常说的三原色(红.绿.蓝),最后一个值是A,也就是Alpha.这4个值都在0~255之间.颜色值越小,表示该颜色越淡,颜色值越大 ...

  10. Android与IOS的UUID的区别

    UUID含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OS ...