自己动手写PHP MVC框架
代码下载: 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",
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: 小程序基础篇之数据解密
- 下一篇: 几种常用的加解密的方法