PHP 原生实现MVC框架

摘要:
$arr_ url=数组('模块'=>空($appPathArr[0])){$arr_url[模块']=$appPathArr[0];空($sappPathArr[1])){$arr_url[控制器']=$sappPatharr[1];
2017-6-5
由于工作需要 打算自己实现一个简单的  MVC框架以完成工作需求
 
初步定义 框架需要完成的工作
1.单入口的路由功能
2.文件的自动载入
3.流水ID的加密以及自动解密
4.MVC文件夹模式
5.通用模板的引用
 
 
单入口的路由实现
 
项目接口的 public 目录中存在一个index.php 文件 作为 项目的唯一入口
 
文件功能主要是  项目全局变量的定义
  1. define ('ROOT_DIR', realpath(dirname(dirname(dirname(__FILE__)))));
以及配置文件和全局方法的引入的引入
  1. require_once LIB_DIR.DS.'configs'.DS.'global.config.php';
  2. require_once LIB_DIR.DS.'configs'.DS.'function.config.php';
为了使 入口文件简单  引入 路由文件
  1. require_once APP_DIR.DS.'router.php';
以及
  1. require_once APP_DIR . DS .'autoload.php';
 
router.php 文件
 
路由文件主要是通过分析 $_SERVER 数组进行 路由的转发  初步定义 项目路径请求的模式   /modules/controller/action
 
由于路由需要兼容几种模式
 
1. 单路径请求   /
2.全路径请求  /index/index/index
3.带参数get请求   /index/index/index?a=1
4.带参数的POST请求 
5.重写路径请求  /index/index/index/a/1
 
需要分布分析  $_SERVER['REQUEST_URI']
 
直接贴上代码
  1. $_RequestUri = $_SERVER['REQUEST_URI'];
  2. //print_r($_SERVER);
  3. $_DocumentPath = $_SERVER['DOCUMENT_ROOT'];
  4. $_FilePath = __FILE__;
  5. $_AppPath = str_replace($_DocumentPath,'', $_FilePath);
  6. $urlPath = $_RequestUri;
  7. // 兼容 ? 号方式的参数
  8. $urlPathArr = explode('?', $urlPath);
  9. $urlPath = $urlPathArr[0];
  10. // $wParams=array();
  11. // if(isset($urlPathArr['1']))
  12. // {
  13. // $wParams=$urlPathArr['1'];
  14. // }
  15. $wParamsArr = array_merge($_GET, $_POST);
  16. $appPathArr = explode(DS, $_AppPath);
  17. for($i =0; $i < count($appPathArr); $i++){
  18. $p = $appPathArr[$i];
  19. if($p){
  20. $urlPath = preg_replace('/^/'. $p .'//','/', $urlPath,1);
  21. }
  22. }
  23. $urlPath = preg_replace('/^//','', $urlPath,1);
  24. $appPathArr = explode("/", $urlPath);
  25. $appPathArrCount = count($appPathArr);
  26. $arr_url = array(
  27. 'modules'=>'index',
  28. 'controller'=>'index',
  29. 'method'=>'index',
  30. 'parms'=> array(),
  31. );
  32. if(!empty($appPathArr[0])){
  33. $arr_url['modules']= $appPathArr[0];
  34. }
  35. if(!empty($appPathArr[1])){
  36. $arr_url['controller']= $appPathArr[1];
  37. }
  38. if(!empty($appPathArr[2])){
  39. $arr_url['method']= $appPathArr[2];
  40. }
  41. $arr_url['parms']= $wParamsArr;
  42. if($appPathArrCount >3){
  43. // 如果大于三 则说明后面是参数
  44. for($i =3; $i < $appPathArrCount; $i +=2){
  45. $arr_temp_hash = array(strtolower($appPathArr[$i])=> $appPathArr[$i +1]);
  46. $arr_url['parms']= array_merge($arr_url['parms'], $arr_temp_hash);
  47. }
  48. }
  49. //print_r($arr_url);
  50. // 转入 controller
  51. $module_name = $arr_url['modules'];
  52. //$class_name = NAME_DIR.DIRECTORY_SEPARATOR.'Controllers'.DIRECTORY_SEPARATOR.ucfirst($module_name).DIRECTORY_SEPARATOR.ucfirst($arr_url['controller']).'Controller';
  53. $class_name = NAME_DIR .'Controllers\'. ucfirst($module_name).'\'. ucfirst($arr_url['controller']).'Controller';
  54. $controller_name = $arr_url['controller'];
  55. $controller_file = CONTROLLERS_DIR . DS . ucfirst($module_name). DS . ucfirst($controller_name).'Controller.php';
  56. //print_r($class_name);
  57. $method_name = $arr_url['method'];
  58. if(file_exists($controller_file)){
  59. //print_r($controller_file);
  60. include $controller_file;
  61. //spl_autoload_register( array($class_name,$method_name) );
  62. //echo 32;die;
  63. $requestArr['path']= $arr_url['modules']. DS . $arr_url['controller']. DS . $arr_url['method'];
  64. $requestArr['server_name']= $_SERVER['SERVER_NAME'];
  65. $requestArr['method']= $_SERVER['REQUEST_METHOD'];
  66. $obj_module =new $class_name($arr_url['parms'], $requestArr);
  67. if(!method_exists($obj_module, $method_name)){
  68. die("要调用的方法不存在");
  69. }else{
  70. //print_r($arr_url['parms']);
  71. $obj_module->$method_name();
  72. }
  73. }else{
  74. die("定义的类不存在");
  75. }
 
当然以上的路由转入 离不开我们的 控制器的父类的支持
在父类的构造方法中,支持参数的传递
  1. $this->params= $this->_addslashes($paramsArr);
  2. $this->autoDeCode();
  3. $this->request = $requestArr;
  4. // //判断是否登录
  5. $path ="/".strtolower($this->getPath());
  6. $this->mtoken=$this->session('mtoken');
  7. if(empty($this->mtoken)&& substr($path,0,11)!='/user/login'){
  8. $url ='/user/login';
  9. echo '<script type="text/javascript">top.window.location.href="http://t.zoukankan.com/kinmos-p-6945885.html'.$url.'";</script>';
  10. }
 
 
而我们的控制器 同样离不开文件的自动加载  不然 命名空间使用不了
 
autoload.php  同样  直接上代码
 
  1. namespaceApplication;
  2. /**
  3. * 自动加载类
  4. * @author kinmos
  5. */
  6. classAutoloader
  7. {
  8. // 应用的初始化目录,作为加载类文件的参考目录
  9. protectedstatic $_appInitPath ='';
  10. /**
  11. * 设置应用初始化目录
  12. * @param string $root_path
  13. * @return void
  14. */
  15. publicstaticfunction setRootPath($root_path)
  16. {
  17. self::$_appInitPath = $root_path;
  18. }
  19. /**
  20. * 根据命名空间加载文件
  21. * @param string $name
  22. * @return boolean
  23. */
  24. publicstaticfunction loadByNamespace($name)
  25. {
  26. // 相对路径
  27. $class_path = str_replace('\', DIRECTORY_SEPARATOR ,$name);
  28. // 先尝试在应用目录寻找文件
  29. if(self::$_appInitPath)
  30. {
  31. $class_file =self::$_appInitPath . DIRECTORY_SEPARATOR . $class_path.'.php';
  32. }
  33. //print_r($class_file);
  34. // 文件不存在,则在上一层目录寻找
  35. if(empty($class_file)||!is_file($class_file))
  36. {
  37. $class_file = APP_DIR.DIRECTORY_SEPARATOR ."$class_path.php";
  38. }
  39. // 找到文件
  40. if(is_file($class_file))
  41. {
  42. // 加载
  43. require_once($class_file);
  44. //print_r($class_file);
  45. if(class_exists($name,false))
  46. {
  47. //echo $name;
  48. returntrue;
  49. }
  50. }
  51. returnfalse;
  52. }
  53. }
  54. // 设置类自动加载回调函数
  55. spl_autoload_register('ApplicationAutoloader::loadByNamespace');
 
 
这样一个完整的路由以及命名空间的自动加载功能都完成了。。
接下来 看下我 顶的目录结构
PHP 原生实现MVC框架第1张
 能自己写的东西 还是自己写的好~~  目录结构 随意定的~
 
然后是数据层
 
数据层的话。嘿嘿。 这边框架没有直接连数据库 而是通过 API的方式  调用 workerman 的微服务进行数据的处理  
这算是偷懒的,后面应该完善,微服务这个东西。。对于开发来说 还是稍微麻烦点的,如果以后简单的项目  就不需要 用了,直接连接数据库 简单暴力 
 
2017-6-12 补充 数据层 链接数据库
 
在目录中添加一个db 的配置文件
 
<?php
namespace Config;
/**
 * mysql配置
 * @author
 */
class Db
{
    // 
    public static $default = array(
        'host'    => '192.168.0.88',
        'port'    => 3306,
        'user'    => 'root',
        'password' => '123456',
        'dbname'  => 'kinwork',
        'charset'    => 'utf8',
    );


}

  配置着本地数据库的路径,另外 这个文件是可以扩展的。便于后面的分库以及分布式的部署

另外在Lib目录中添加了三个数据库操作文件

Db 文件,DbTable  DbConnection 

Db 是操作数据库的表层文件,DbTable 文件是实际进行数据库操作的类  DbConnection 是进行数据库链接的类 进行数据库连接池的 管理

然后在我们的Models 文件夹中添加了 Db文件夹 对应每个数据库表的一个文件 

<?php

namespace HttpModelsDb;

use LibDbTable;

/**
 * 
 */
class User extends DbTable
{
    protected $_name = 'user';

    protected $_primary = 'id';

    protected $_db = 'default';

    /*
    * 构造函数
    */
    public function __construct()
    {
    }
}

其中 _name 是表名  _db 是对应的config的数据库配置  兼容数据库的分库应用

在实际使用的时候,

$DbTable_User = Db::DbTable('User');

        // 查询所有数据
        $list=$DbTable_User->getList('1=1');
        print_r($list);
        // 查询 分页数据
        $pagelist=$DbTable_User->pageList('1=1','*');
        print_r($pagelist);

        // 查询单条数据
        $row=$DbTable_User->getRow(array('id'=>1));
        print_r($row);

        // 添加数据
        $id=$DbTable_User->insert(array('name'=>'kinmos'));
        print_r($id);

        // 修改数据
        $status=$DbTable_User->update(array('name'=>'kinmos'),array('id'=>1));
        print_r($status);

        // 删除数据
        $status=$DbTable_User->delete(array('id'=>15));
        print_r($status);

至此,数据层完成。

 
 
最后是视图 层
 
视图的直接用的php的视图,而没有去用 类似   smarty 的模板引擎   额,个人感觉 php自己就是一个不错的 模板引擎啊。。就不多引用别的东西了~~哈哈 
 
视图的话,在我们的Views文件夹中,然后模板就有一个问题 就是模板的数据渲染
 
在父类 的ActionController.php 中封装了一个  view 方法 用来实现该功能
 
  1. publicfunction getViewUrl(){
  2. $this->urlPath = $this->getPath();
  3. $Path_arr = explode("/",$this->urlPath);
  4. $Module = isset($Path_arr[0])&&$Path_arr[0]!=''?$Path_arr[0]:"Index";
  5. $Controller = isset($Path_arr[1])&&$Path_arr[1]!=''?$Path_arr[1]:"Index";
  6. $Method = isset($Path_arr[2])&&$Path_arr[2]!=''?$Path_arr[2]:"index";
  7. return ucfirst($Module).'/'.ucfirst($Controller).'/'.$Method;
  8. }
  9. /*
  10. * 跳到视图页面
  11. * $data = array() 数组格式 带到视图页面的参数
  12. */
  13. publicfunction view($data = array()){
  14. $view = VIEWS_DIR . DS.$this->getViewUrl().'.php';
  15. extract($data, EXTR_OVERWRITE);
  16. ob_start();
  17. file_exists($view)?require $view :exit($view .' 不存在');
  18. $content = ob_get_contents();
  19. return $content;
  20. }
 
这样在我们的控制器中 当需要使用到 视图 就可以直接 $this->view($data); 就可以带出数据到视图模板中了
 
然后模板的通用引用  也就简单了
在我们的视图文件夹中  存在 一个Pub文件夹 存放所有的通用模板
  1. <?php include_once(VIEWS_DIR .DS."Pub".DS."header.php");?>
 
  
至此,一个简单的 phpMVC框架就搭建好了~
然后秉承以前左大侠的教诲  系统安全性  是衡量一个系统好坏的重要因素
 
故而,这里简单做了些 安全性的控制 
1.防注入
2.关键信息的加密
 
防注入  addslashes
  1. publicfunction _addslashes($param_array){
  2. if(is_array($param_array)){
  3. foreach($param_array as $key=>$value){
  4. if(is_string($value))
  5. $param_array[$key]= addslashes(trim($value));
  6. }
  7. }else{
  8. $param_array = addslashes($param_array);
  9. }
  10. return $param_array;
  11. }
 
关键信息的加密的话,主要是针对 流水ID  自增字段
 
主要在 我们的公共方法  function.php 中封装了两个方法  用于加密和解密
这里的话,用到的对称加密方式, AES的对称加密    (关键信息被修改掉啦)
  1. functionEncode($str)
  2. {
  3. if($str ==''){
  4. return'';
  5. }
  6. $aes =newAes();
  7. return urlencode($aes->encrypt($str));
  8. }
  9. functionDecode($str)
  10. {
  11. if($str ==''){
  12. return'';
  13. }
  14. $aes =newAes();
  15. if(strpos($str,'%'))
  16. return $aes->decrypt(urldecode($str));
  17. else
  18. return $aes->decrypt($str);
  19. }
 
AES 方法类
  1. classAes
  2. {
  3. private $_privateKey ="kinmoshelloword&*#";
  4. private $_byt = array(0x19,0x34,0x56,0x78,0x90,0xAB,0xCD,0xEF,0x12,0x34,0x56,0x78,0x90,0xAB,0xCD,0xEF);
  5. publicfunction toStr($bytes)
  6. {
  7. $str ='';
  8. foreach($bytes as $ch){
  9. $str .= chr($ch);
  10. }
  11. return $str;
  12. }
  13. publicfunction encrypt($data)
  14. {
  15. $vi = $this->toStr($this->_byt);
  16. $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->_privateKey, $data, MCRYPT_MODE_CBC, $vi);
  17. return base64_encode($encrypted);
  18. }
  19. publicfunction decrypt($data)
  20. {
  21. $vi = $this->toStr($this->_byt);
  22. $encryptedData = base64_decode($data);
  23. $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->_privateKey, $encryptedData, MCRYPT_MODE_CBC, $vi);
  24. //print_r($decrypted);
  25. return rtrim($decrypted,"

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【H5】15 表单 其四 数据发送Ubuntu 终端命令速查表下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

[开源 .NET 跨平台 Crawler 数据采集 爬虫框架: DotnetSpider] [一] 初衷与架构设计

[DotnetSpider 系列目录] 一、初衷与架构设计 二、基本使用 三、配置式爬虫 四、JSON数据解析与配置系统 五、如何做全站采集 为什么要造轮子 同学们可以去各大招聘网站查看一下爬虫工程师的要求,大多是招JAVA、PYTHON,甚至于还有NODEJS,C++;再或者去开源中国查询C#的爬虫项目,仅有几个非常简单或是几年没有更新的项目。 而单...

Oracle Sql 胡乱记

/Oracle查询优化改写/ --1、coalesce 返回多个值中,第一个不为空的值 select coalesce('', '', 's') from dual; --2、order by -----dbms_random.value 生产随机数,利用随机数对查询结果进行随机排序 select * from emp order by dbms_rand...

maven 命令小记

mvn help:system mvn clean compile mvn clean test                            测试 mvn clean package                    打包成jar mvn clean install mvn dependency:tree 列出jar之间依赖引入的jar ma...

Electron截屏功能

# Electron截屏功能 window下增加该功能,可以调用三方的exe文件然后通过node.js的原生模块,execFile启动该exe文件。 mac下则可以通过screencapture 来调用系统的截屏功能来实现 globalShortcut.register('CommandOrControl+Alt+Z', function () { i...

Linux程序调试查看二进制文件

http://blog.sina.com.cn/s/blog_7a2fc53a0100y54h.html 一,二进制文件的类型       Linux下的二进制文件是ELF格式的,主要有目标文件、静态链接库文件、动态链接库文件、可执行文件和core dump文件。可以使用如下命令查看其类型:       file  文件名。       我们还是以之前...

Mac Terminal菜鸟篇之使用unrar解压rar文件

在Mac上的归档工具不能够解压rar文件,这时可以使用终端中的unrar来解决问题。 步骤如下: 1.使用Homebrew安装unrar(有关Homebrew的安装和使用见Homebrew) [plain] view plain copy  $ brew install unrar   ==> Downloading http://www.r...