• 构建自己的PHP框架(视图装载)


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

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

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

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

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

    基础准备

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

    启动流程组件化

    将  public/index.php  里面的代码分离一部分到启动器(bootstrap),新建  MFFC/bootstrap.php  文件:

    <?php
    
    use IlluminateDatabaseCapsuleManager 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 IlluminateDatabaseCapsuleManager 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 WhoopsRun;
    
    $whoops->pushHandler(new WhoopsHandlerPrettyPageHandler);
    
    $whoops->register();

    下面我们将增加路由配置中  无匹配项  的错误页面,修改  config/routes.php :

    <?php
    
    use NoahBuscherMacawMacaw 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  视图文件,将最终运算结果发送给浏览器,流程结束。
  • 相关阅读:
    poj 2482 Stars in Your Window + 51Nod1208(扫描线+离散化+线段树)
    bzoj 1036: [ZJOI2008]树的统计Count (树链剖分+线段树 点权)
    树链剖分+线段树 单点修改 区间求和 模板
    bzoj 2124 等差子序列 (线段树维护hash)
    hdu 5638 Toposort (拓扑排序+线段树)
    hdu 5195 DZY Loves Topological Sorting (拓扑排序+线段树)
    Codeforces Round #250 (Div. 1) D. The Child and Sequence(线段树)
    hive 显示当前数据库名
    【Linux】ssh-keygen 的使用方法及配置 authorized_key s两台linux机器相互认证
    python email 模块
  • 原文地址:https://www.cnblogs.com/evai/p/6212844.html
Copyright © 2020-2023  润新知