牛骨文教育服务平台(让学习变的简单)
博文笔记

自己动手写PHP MVC框架

创建时间:2017-02-18 投稿人: 浏览次数:3068

代码下载: https://github.com/yuansir/tiny-php-framework

PHP的框架众多,对于哪个框架最好,哪个框架最烂,是否应该用框架,对于这些争论在论坛里面都有人争论,这里不做评价, 
个人觉得根据自己需求,选中最佳最适合自己MVC框架,并在开发中能够体现出敏捷开发的效果就OK了,作为一个PHPer要提高自己的对PHP和MVC的框架的认识,所以自己写一个MVC框架是很有必要的, 
即使不是很完善,但是自己动手写一个轻量简洁的PHP MVC框架起码对MVC的思想有一定的了解,而且经过自己后期的完善会渐渐形成一个自己熟悉的一个PHP框架。 

来写一个PHP MVC框架开发的简明教程,首先声明,教程里面的框架不是一个完善的框架,只是一种思路,当然每个人对MVC框架实现的方法肯定是有差异的,希望高手多提意见多指正,和我一样的菜鸟多讨论多交流,刚接触MVC的PHPer多学习。 

首先,我们在项目中建立如下目录和文件: 

app 
|-controller    存放控制器文件 
|-model        存放模型文件 
|-view        存放视图文件    
|-lib        存放自定义类库 
|-config    存放配置文件 
|--config.php   系统配置文件 
|-system    系统核心目录 
|-index.php    入口文件 

新件的index.php为入口文件,我们这里采用单一入口,入口文件的内容很简单: 

<?php 
/** 
 * 应用入口文件 
 * @copyright   Copyright(c) 2011 
 * @author      yuansir <yuansir@live.cn/yuansir-web.com> 
 * @version     1.0 
 */ 
require dirname(__FILE__)."/system/app.php"; 
require dirname(__FILE__)."/config/config.php"; 
Application::run($CONFIG); 

入口文件主要做了2件事,第一引入系统的驱动类,第二是引入配置文件,然后运行run()方法,传入配置作为参数,具体这2个文件是什么内容,我们接下来继续看。 

先看一下config/config.php文件,里面其实是一个$CONFIG变量,这个变量存放的全局的配置: 

<?php 
/** 
 * 系统配置文件 
 * @copyright   Copyright(c) 2011 
 * @author      yuansir <yuansir@live.cn/yuansir-web.com> 
 * @version     1.0 
 */ 

/*数据库配置*/ 
$CONFIG["system"]["db"] = array( 
    "db_host"           =>      "localhost", 
    "db_user"           =>      "root", 
    "db_password"       =>      "", 
    "db_database"       =>      "app", 
    "db_table_prefix"   =>      "app_", 
    "db_charset"        =>      "urf8", 
    "db_conn"           =>      "",             //数据库连接标识; pconn 为长久链接,默认为即时链接 
    
); 

/*自定义类库配置*/ 
$CONFIG["system"]["lib"] = array( 
    "prefix"            =>      "my"   //自定义类库的文件前缀 
); 

$CONFIG["system"]["route"] = array( 
    "default_controller"             =>      "home",  //系统默认控制器 
    "default_action"                 =>      "index",  //系统默认控制器 
    "url_type"                       =>      1          /*定义URL的形式 1 为普通模式    index.php?c=controller&a=action&id=2 
                                                         *              2 为PATHINFO   index.php/controller/action/id/2(暂时不实现)              
                                                         */                                                                           
); 

/*缓存配置*/ 
$CONFIG["system"]["cache"] = array( 
    "cache_dir"                 =>      "cache", //缓存路径,相对于根目录 
    "cache_prefix"              =>      "cache_",//缓存文件名前缀 
    "cache_time"                =>      1800,    //缓存时间默认1800秒 
    "cache_mode"                =>      2,       //mode 1 为serialize ,model 2为保存为可执行文件    
); 

我这里有意识的定义$CONFIG["system"]数组表示是系统的配置文件,当然你可以在里面定义$CONFIG["myconfig"]为表示在定义的配置,以后在程序的控制器,模型,视图中来调用,都个很自由。 
具体配置值代表什么意思注视很清楚了,下面的如果程序中有详细注释的我就不解释啦,呵呵 

再来看一下system/app.php文件,主要是干嘛的: 

<?php 
/** 
 * 应用驱动类 
 * @copyright   Copyright(c) 2011 
 * @author      yuansir <yuansir@live.cn/yuansir-web.com> 
 * @version     1.0 
 */ 
define("SYSTEM_PATH", dirname(__FILE__)); 
define("ROOT_PATH",  substr(SYSTEM_PATH, 0,-7)); 
define("SYS_LIB_PATH", SYSTEM_PATH."/lib"); 
define("APP_LIB_PATH", ROOT_PATH."/lib"); 
define("SYS_CORE_PATH", SYSTEM_PATH."/core"); 
define("CONTROLLER_PATH", ROOT_PATH."/controller"); 
define("MODEL_PATH", ROOT_PATH."/model"); 
define("VIEW_PATH", ROOT_PATH."/view"); 
define("LOG_PATH", ROOT_PATH."/error/"); 
final class Application { 
        public static $_lib = null; 
        public static $_config = null; 
        public static function init() { 
                self::setAutoLibs(); 
                require SYS_CORE_PATH."/model.php"; 
                require SYS_CORE_PATH."/controller.php"; 
                
        } 
        /** 
         * 创建应用 
         * @access      public 
         * @param       array   $config 
         */ 
        public static function run($config){ 
                self::$_config = $config["system"]; 
                self::init(); 
                self::autoload(); 
                self::$_lib["route"]->setUrlType(self::$_config["route"]["url_type"]); 
                $url_array = self::$_lib["route"]->getUrlArray(); 
                self::routeToCm($url_array); 
        } 
        /** 
         * 自动加载类库 
         * @access      public 
         * @param       array   $_lib 
         */ 
        public static function autoload(){ 
                foreach (self::$_lib as $key => $value){ 
                        require (self::$_lib[$key]); 
                        $lib = ucfirst($key); 
                        self::$_lib[$key] = new $lib;                      
                } 
                //初始化cache 
                if(is_object(self::$_lib["cache"])){ 
                        self::$_lib["cache"]->init( 
                                ROOT_PATH."/".self::$_config["cache"]["cache_dir"], 
                                self::$_config["cache"]["cache_prefix"], 
                                self::$_config["cache"]["cache_time"], 
                                self::$_config["cache"]["cache_mode"] 
                                ); 
                } 
        } 
        /** 
         * 加载类库 
         * @access      public  
         * @param       string  $class_name 类库名称 
         * @return      object 
         */ 
        public static function newLib($class_name){ 
                $app_lib = $sys_lib = ""; 
                $app_lib = APP_LIB_PATH."/".self::$_config["lib"]["prefix"]."_".$class_name.".php"; 
                $sys_lib = SYS_LIB_PATH."/lib_".$class_name.".php"; 
                
                if(file_exists($app_lib)){ 
                        require ($app_lib); 
                        $class_name = ucfirst(self::$_config["lib"]["prefix"]).ucfirst($class_name); 
                        return new $class_name; 
                }else if(file_exists($sys_lib)){ 
                        require ($sys_lib); 
                        return self::$_lib["$class_name"] = new $class_name; 
                }else{ 
                        trigger_error("加载 ".$class_name." 类库不存在"); 
                } 
        } 
        /** 
         * 自动加载的类库 
         * @access      public 
         */ 
        public static function setAutoLibs(){ 
                self::$_lib = array( 
                    "route"              =>      SYS_LIB_PATH."/lib_route.php", 
                    "mysql"              =>      SYS_LIB_PATH."/lib_mysql.php", 
                    "template"           =>      SYS_LIB_PATH."/lib_template.php", 
                    "cache"           =>      SYS_LIB_PATH."/lib_cache.php", 
                    "thumbnail"           =>      SYS_LIB_PATH."/lib_thumbnail.php" 
                );      
        } 
        /** 
         * 根据URL分发到Controller和Model 
         * @access      public 
         * @param       array   $url_array     
         */ 
        public static function routeToCm($url_array = array()){ 
                $app = ""; 
                $controller = ""; 
                $action = ""; 
                $model = ""; 
                $params = ""; 
                
                if(isset($url_array["app"])){ 
                        $app = $url_array["app"]; 
                } 
                
                if(isset($url_array["controller"])){ 
                        $controller = $model = $url_array["controller"]; 
                        if($app){ 
                                $controller_file = CONTROLLER_PATH."/".$app."/".$controller."Controller.php"; 
                                $model_file = MODEL_PATH."/".$app."/".$model."Model.php"; 
                        }else{ 
                                $controller_file = CONTROLLER_PATH."/".$controller."Controller.php"; 
                                $model_file = MODEL_PATH."/".$model."Model.php"; 
                        } 
                }else{ 
                        $controller = $model = self::$_config["route"]["default_controller"]; 
                        if($app){ 
                                $controller_file = CONTROLLER_PATH."/".$app."/".self::$_config["route"]["default_controller"]."Controller.php"; 
                                $model_file = MODEL_PATH."/".$app."/".self::$_config["route"]["default_controller"]."Model.php"; 
                        }else{ 
                                $controller_file = CONTROLLER_PATH."/".self::$_config["route"]["default_controller"]."Controller.php"; 
                                 $model_file = MODEL_PATH."/".self::$_config["route"]["default_controller"]."Model.php"; 
                        } 
                } 
                if(isset($url_array["action"])){ 
                        $action = $url_array["action"]; 
                }else{ 
                        $action = self::$_config["route"]["default_action"]; 
                } 
                
                if(isset($url_array["params"])){ 
                        $params = $url_array["params"]; 
                } 
                if(file_exists($controller_file)){ 
                       if (file_exists($model_file)) { 
                                require $model_file; 
                        } 
                        require $controller_file; 
                        $controller = $controller."Controller"; 
                        $controller = new $controller; 
                        if($action){ 
                                if(method_exists($controller, $action)){ 
                                        isset($params) ? $controller ->$action($params) : $controller ->$action(); 
                                }else{ 
                                        die("控制器方法不存在"); 
                                } 
                        }else{ 
                                die("控制器方法不存在"); 
                        } 
                }else{ 
                        die("控制器不存在"); 
                } 
        } 



我叫它框架驱动类,也许不合适,但是我是这样理解的,它用来启动这个框架,做好一些初始化的工作,下面我来详细分析一下每个方法的功能: 
1.首先时定义了一些常量,很明了,不解释了 
2.setAutoLibs 这个方法其实就是设定那些是系统启动时自动加载的类库,类库文件都存放在SYS_LIB_PATH下面,以lib_开头的,当然这里你可以根据自己的规则来命名 
3.autoload 这个方法就是用来引入你要自动加载的类,然后来实例化,用$_lib数组来保存类的实例,比如$lib["route"]是system/lib/lib_route.php中lib_route类的实例 
4.newLib 这个方法是用来加载你自定义的类的,自定义类存放在根目录下的lib中,但是自定义的类的文件前缀是你自己定义的,看系统配置文件里面有,我定义的是my,这样我就可以在lib 
    目录下新建一个自定义的类了,比如 my_test.php 
    <?php 
    class MyTest { 
            function __construct() { 
                         echo "my lib test"; 
            } 
    } 
为什么类名这样命名,看下newLib方法的实现就知道,其实这些你完全可以定义自己的规则,这个方法会首先去着lib下面有没有这个类,如果有就会引入实例化,如果没有就去找系统目录下面的类,有就实例化 
5.init 就是一个初始化的方法,里面其实就是加载自动加载的类,以及引入核心控制器和核心模型,这个2个核心文件过会我们再来分析 
6.run 方法就是启动这个框架的了,里面的最后2步很重要,就是获取URL然后拆分成一个数组的形似,然后由routeToCm来分发到Controller和Model 
7.routeToCm 很重要,根据URL分发到Controller和Model,这个我们过会来说 

在run方法中 
                self::$_lib["route"]->setUrlType(self::$_config["route"]["url_type"]); //设置url的类型 
                $url_array = self::$_lib["route"]->getUrlArray();                      //将url转发成数组 
好吧,我们来看下route的系统类到底做了说明 

<?php 
/** 
 * URL处理类 
 * @copyright   Copyright(c) 2011 
 * @author      yuansir <yuansir@live.cn/yuansir-web.com> 
 * @version     1.0 
 */ 
final class Route{ 
        public $url_query; 
        public $url_type; 
        public $route_url = array(); 


        public function __construct() { 
                $this->url_query = parse_url($_SERVER["REQUEST_URI"]);      
        } 
        /** 
         * 设置URL类型 
         * @access      public 
         */ 
        public function setUrlType($url_type = 2){ 
                if($url_type > 0 && $url_type <3){ 
                        $this->url_type = $url_type; 
                }else{ 
                        trigger_error("指定的URL模式不存在!"); 
                } 
        } 

        /** 
         * 获取数组形式的URL  
         * @access      public 
         */ 
        public function getUrlArray(){ 
                $this->makeUrl(); 
                return $this->route_url; 
        } 
        /** 
         * @access      public 
         */ 
        public function makeUrl(){ 
                switch ($this->url_type){ 
                        case 1: 
                                $this->querytToArray(); 
                                break; 
                        case 2: 
                                $this->pathinfoToArray(); 
                                break; 
                } 
        } 
        /** 
         * 将query形式的URL转化成数组 
         * @access      public 
         */ 
        public function querytToArray(){ 
                $arr = !empty ($this->url_query["query"]) ?explode("&", $this->url_query["query"]) :array(); 
                $array = $tmp = array(); 
                if (count($arr) > 0) { 
                        foreach ($arr as $item) { 
                                $tmp = explode("=", $item); 
                                $array[$tmp[0]] = $tmp[1]; 
                        } 
                        if (isset($array["app"])) { 
                                $this->route_url["app"] = $array["app"]; 
                                unset($array["app"]); 
                        } 
                        if (isset($array["controller"])) { 
                                $this->route_url["controller"] = $array["controller"]; 
                                unset($array["controller"]); 
                        } 
                        if (isset($array["action"])) { 
                                $this->route_url["action"] = $array["action"]; 
                                unset($array["action"]); 
                        } 
                        if(count($array) > 0){ 
                                $this->route_url["params"] = $array; 
                        } 
                }else{ 
                        $this->route_url = array(); 
                }   
        } 
        /** 
         * 将PATH_INFO的URL形式转化为数组 
         * @access      public 
         */ 
        public function pathinfoToArray(){ 
                
        } 



注意querytToArray方法,将将query形式的URL转化成数组,比如原来是localhost/myapp/index.php/app=admin&controller=index&action=edit&id=9&fid=10 这样的url就会被转发成如下的数组 
array( 
    "app"        =>"admin", 
    "controller"    =>"index", 
    "action"    =>"edit", 
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。