• MVC框架,see again


    前言

    写这篇文章的初衷,是因为群里因为一个美女工程师而引发了一阵骚动,作为一个看到美女就眼睛放光的人来说,怎么会放过这么好欣赏美女的机会[色]。于是大家将美女的github给翻出来了,我有幸看到了美女写的php框架,download下来,认真向美女学习[崇拜ing]。

    美女的框架前言是这样:

    写此框架的初衷是因为发现现在流行的php框架都异常臃肿,臃肿的原因是因为框架都封装了许多的功能,这些功能我在实际开发中很多并未使用到,这就造成使用框架的性能浪费。

    基于此,于2014年的五一期间写了这个框架,众多流行框架都有一个自己的名字,我就为这个框架取名叫“one”。

    刚开始参加工作的时候,因为技术基础很烂,所以被分配到去做管理平台的网页。那时候被同事问及知道什么是MVC吗,我说听过,不知道确切的MVC是什么。于是同事介绍哪里负责什么功能,什么功能代码应该放在什么文件夹下。第一次就是简单在index.html添加一个菜单,通过点击这个菜单跳转到某一个页面。

    此框架虽然简单,但是其提取出来了核心骨架,虽然不适合真正去建立大型网站,但是适合初学者去了解MVC以及网站的基本搭建。

    代码范例

    一个美女的简单的PHP框架one---- https://github.com/linsunny/one

    想要鉴定美女的童鞋,可以发挥自己的想象力网上百度下。

    代码架构

    框架的架构如下图所示,library文件夹保存的是核心类,也就是一些基类,比如model,view,controller的基类,mysql的sql操作类,负责启动框架和统筹全局的core类。index.php作为入口文件,view存储的主要为视图文件,如html,或者是smarty的tpl文件。st为静态文件,比如js,css,image等。model保存的是业务逻辑处理代码文件,负责处理用户请求,保存用户数据到db,cache,以及访问db,cache读取用户数据等。controller保存的是负责接收用户请求,并调用model处理用户请求,并将用户请求结果在view中显示。配置文件保存的是各种资源(db,smarty,router等)的相关配置。后续会在代码中体现这些文件是如何各司其责的。

      

    本部分将通过分析从浏览器输入一个url链接到页面显示的整个过程,了解本框架的数据流。

    在本机上部署好xmapp环境后,将代码放到/var/www/html/目录下

    url: http://beauty/one-branch/index.php?c=index&a=init

    在浏览器中输入如上链接,点击enter,可以看到如下的页面。

    数据流

    • 域名解析服务器ip和port

      如在浏览器中输入网址:http://beauty/one-branch/index.php?c=index&a=init

    • 通过web服务器找到相应的代码路径

      http解析上面的网址,域名beauty解析到服务器172.16.0.0:80,然后在172.16.0.0这台服务器上的apache的配置文件(如下所示),根据port找到根目录路径DocumentRoot。即将http://beauty 映射到 172.16.0.0的目录/var/www/html下,从而将http://beauty/one-branch/index.php?c=index&a=init    映射到   var/www/html/one-branch/index.php?c=index&a=init

    配置文件的截图如下:

     <VirtualHost *:80>
         DocumentRoot /var/www/html
         ServerName beauty
    
     </VirtualHost>

    index.php的代码如下:

    <?php
    
    define('ROOT_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR );
    define('CONTROLLER_PATH', ROOT_PATH . 'controller' . DIRECTORY_SEPARATOR);
    define("VIEW_PATH", ROOT_PATH ."view" . DIRECTORY_SEPARATOR);
    define("CACHE_PATH", ROOT_PATH ."cache" . DIRECTORY_SEPARATOR);
    define("MODEL_PATH", ROOT_PATH ."model" . DIRECTORY_SEPARATOR);
    define("CONFIG_PATH", ROOT_PATH ."config" . DIRECTORY_SEPARATOR);
    define("LIBRARY_PATH", ROOT_PATH ."library" . DIRECTORY_SEPARATOR);
    define("ST_PATH", ROOT_PATH ."st" . DIRECTORY_SEPARATOR);
    
    include CONFIG_PATH . "config.php";
    include LIBRARY_PATH . "core.php";
    
    Core::run($config);  // main函数,入口函数

    在index.php中除了定义一些全局宏变量,包含配置文件和核心类文件。只有一句运行代码,调用了核心类Core的run函数。

    我们看看框架启动入口主函数Core::run($config)函数主要干啥子呢?

        /**
         * 框架启动逻辑
         *
         */
        public static function run($config) {
            //获取配置文件
            self::$_config = $config;
    
            //获取路由信息
            $router = self::_parseUrl();
    
            //自动加载核心类    
            self::autoLoad();
    
            //调用控制器及其方法
            self::initController($router);
        }

    1. 获取配置文件

      config配置主要配置:

    • Controller的路由信息
    • Model中需要的db和redis信息
    • View中的模版引擎的相关信息
    <?php
    /**
     * config.php
     *
     * 此配置文件在框架载入中就已经加载
     *
     * @author linsunny<hi_linsunny@163.com>
     */
    
    /**
     * 路由配置信息
     * $config[router]['show'][1] = xx.com/index.php?c=controller&a=action          
     * $config[router]['show'][2] = xx.com/index.php/controller/action
     */
    $config['router']  = array(
        'default_controller'    =>    'index',
        'default_action'        =>    'init',
        'show'                    =>    2,
    );
    /**
     * 模板引擎配置信息
     * $config['smarty']['suffix']表示模板文件的后缀名
     * $config['smarty']['isCache']表示是否用缓存 (默认不启动)
     * $config['smarty']['cacheLeftTime']表示缓存有效期 (默认保存3600秒)
     */
    $config['smarty'] = array(
        'template_dir'    =>    VIEW_PATH,
        'compile_dir'    =>    CACHE_PATH . "compile" . DIRECTORY_SEPARATOR,
        'cache_dir'    =>    CACHE_PATH . "cache" . DIRECTORY_SEPARATOR,
        'suffix'        =>    ".htm",
        'isCache'        =>    true,
        'cacheLeftTime'    =>    3600,
    );
    
    
    /**
     *数据库配置
     *$config['db']['conn']表示数据库连接标识; pconn 为长久链接,默认为即时链接
     */
    $config['db'] = array(
        'host'           =>      'localhost',
        'user'           =>      'root',
        'password'       =>      '',
        'database'       =>      'idou',
        'table_prefix'   =>      'v1_',
        'charset'        =>      'urf8',
        'conn'           =>      '', 
        'port'             =>         80                   
    );

    2.  解析Url获取代码路由信息

    解析参数找到相应的Controller类以及该类的运行函数,以及函数参数

    运行这个脚本 var/www/html/one-branch/index.php,然后解析后面?c=index&a=init带的参数,第一个参数以?分割,后续以&分割。

    获取参数c=index,a=init。

    3. 自动加载核心类

    加载之前core文件夹中的核心类(model,view,controller,mysql),将这些代码路径require或者include进来。

    4. 调用用户请求的控制器及其方法,处理用户请求并显示请求结果

    c标识controller, 故该类是indexController,c=index,a=init说明在Controller模块中找到indexController类,实例化indexController对象,并且调用该类的indexController_object->init($params_arr)。

        /**
         * 初始化控制器
         * 调用对应控制器以及方法
         * @class Core  
         */
        public static function initController($router) {
            static $codeAr = array();//定义静态变量储存数组,类似单例模式,这里用来存储控制方法逻辑
            $key = $router['c'] . "_" . $router['a'];
            
            $controller = $router['c'] . "Controller";
            $action = $router['a'] . "Action";
    
            //加载控制器文件
            $controller_path = CONTROLLER_PATH . $controller . ".php";
            self::loadFile($controller_path);
    
            //创建控制器
            $object = new $controller();
            if(method_exists($object, $action)){
                $codeAr[$key] = $object->$action();
            }else{
                self::error("控制器方法不存在!");
            }
            return $codeAr[$key];
        }

    接下来调用相应的indexController中的init函数。在init函数中先调用model函数读写db获得相应数据,再通过loadView创建单例对象view,将相应数据assign给view,最后将view进行display。

     indexController的函数如下:

    <?php
    class indexController extends Controller{
    
            public function initAction(){
                    
                    // 调用model相应函数,将当前用户信息写入DB
                    $this->saveAction();
                    
                    // 创建view的单例
                    $this->loadView();   
                  
                    $items = array();
                    $items[1] = "路由解析";
                    $items[2] = "控制器分配";
                    $items[3] = "单例实现";
                    $items[4] = "类自动加载";
                    $items[5] = "唯一入口";
    
                    // 赋值到view的相应位置
                    $this->view->assign('items', $items);
                    $this->view->assign('hello_str', "hello world");
                    $this->view->assign('date', date("Y-m-d H:i:s"));
     
                    // 渲染view
                    $this->view->display('index.htm');
    
            }
    
            public function saveAction(){
                    $array = array('zero', '123456');
                    //加载adminModel
                    $model = $this->model('admin');
    
                    //调用mysql的add方法
                    $flag = $model->add($array);
    
            }
    }

    上面的controller的initAction主要是调用了model的方法函数,将获取到的值assign赋值给相应的变量,同时将页面进行display。上面的modelAction,assign,diplay三个函数相对于难于理解的就是display的函数。display函数主要是渲染html,将js,css,img,以及一些变量值以及一些php逻辑填充到html,浏览器对渲染后的html进行相应的解析。display的主要数据流程如下图所示,可以参照下面的代码来梳理display的流程图。

    /**
         * 页面显示
         * 传入模板文件名,返回编译文件路径
         * @access public
         * @return string
         */ 
        public function display($file) { 
            if(!$file) Core::error( '参数错误' ); 
            $view_file = self::$config['template_dir'] . $file;
            if( !file_exists($view_file) ) Core::error( $view_file . '模板文件不存在' ); 
            $cache_file = self::$config['cache_dir'] . md5($file);
            //当缓存文件存在且不过期时直接从缓存文件读取内容
            if( $this->checkCacheExpire($cache_file, self::$config['cacheLeftTime']) ){
                echo $this->readFile($cache_file);
            }else {
                extract($this->var);//注册key=value
    
                $view_content = $this->readFile($view_file);//读文件
                $view_content = $this->replaceTag($view_content);//替换标签
    
                $temp_file = self::$config['compile_dir'] . md5($file).'.htm.php';
                $this->writeFile($temp_file, $view_content);
                
                ob_start();
                include($temp_file);
                $content = ob_get_contents();
                ob_end_clean() ;
                if (self::$config['isCache']){                          
                    $this->writeFile($cache_file,$content) ;//编译好的内容写入缓存文件
                }
                echo $content ;
            }
        }

    之后附上本程序的流程图以及本程序的日志文件。

    后记

     再此附上一张鸟哥的框架流程图。

    也有人说框架应该叫做MVCD,D指的是Data,附上MVCD的一张图,说清楚1,2,3,4这几个步骤。

     

    参考资料:

    http://www.laruence.com/manual

     https://github.com/linsunny/one

    Question:

    url链接除了可以带c和a参数以外,还可以带其他参数作为action函数的参数,所以parseUrl和initController的函数都还可以进行扩展。

    可以扩展通过model读取到数据,然后赋值给view中的参数显示出来。---done

    如果现在要求我自己模拟写一个简单的博客网站后台框架,我会从哪些方面来写,先完成哪些功能,再完成哪些功能。

    我希望首页出现,右边是一个简单的导航菜单,或者右边是一个导航菜单,各个导航菜单之间是可以任意跳转的。---done

    记得上大学的时候,自己用html和css写了一个静态的网站,希望有机会还能找到,如果能找到,就自己在那个网站上试试。

    应该是有一个html页面有一个css,还有一个image和vedio什么的。---可以尝试加一个dancing vedio

    想想如果要用mysql的类,如何用composer加载依赖的类库。---没有使用composer

    2016.01.12 在此博文的基础上,尝试了解决上述问题。请参见此博文---模仿写一个小型网站框架 

  • 相关阅读:
    vue3.0提前了解系列一 通过cli快速搭建一个3.0项目
    vscode卡的飞起解决办法-其中之一
    常用正则表达式整理
    jq-outerhtml不能执行新元素内部的js解决方案
    前端面试题(亲身面试经验)
    MAC上Cisco AnyConnect删除不干净,造成无法重新安装的解决办法
    vue需要知道哪些才能算作入门以及熟练
    jquery版本轮播图(es5版本,兼容高)
    webpack4常用片段
    前端速度优化
  • 原文地址:https://www.cnblogs.com/TsingLo/p/4668938.html
Copyright © 2020-2023  润新知