PHP 原生實現MVC框架

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="'.$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('\Application\Autoloader::loadByNamespace');
 
 
這樣一個完整的路由以及命名空間的自動加載功能都完成了。。
接下來 看下我 頂的目錄結構
 能本身寫的東西 仍是本身寫的好~~  目錄結構 隨意定的~
 
而後是數據層
 
數據層的話。嘿嘿。 這邊框架沒有直接連數據庫 而是經過 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',
    );


}

  配置着本地數據庫的路徑,另外 這個文件是能夠擴展的。便於後面的分庫以及分佈式的部署javascript

另外在Lib目錄中添加了三個數據庫操做文件php

Db 文件,DbTable  DbConnection java

Db 是操做數據庫的表層文件,DbTable 文件是實際進行數據庫操做的類  DbConnection 是進行數據庫連接的類 進行數據庫鏈接池的 管理mysql

 

而後在咱們的Models 文件夾中添加了 Db文件夾 對應每一個數據庫表的一個文件 sql

<?php

namespace Http\Models\Db;

use Lib\DbTable;

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

    protected $_primary = 'id';

    protected $_db = 'default';

    /*
    * 構造函數
    */
    public function __construct()
    {
    }
}

其中 _name 是表名  _db 是對應的config的數據庫配置  兼容數據庫的分庫應用shell

 

在實際使用的時候,數據庫

$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,"\0");
  26. }
  27. }
 
對於 Encode 方法 和Decode 方法的  url_decode  和url_encode 主要是爲了防止 路徑的轉義致使的加解密不正確~ 
另外 主要是針對 流水ID的  因此 在咱們的基類中添加了一個自動解密的方法
 
  1. protectedfunction autoDeCode(){
  2. if(isset($this->params["id"]))
  3. {
  4. $id=$this->params["id"];
  5. //print_r($id);die;
  6. //print_r($_GET);
  7. if(!empty($id)&&!preg_match("/^[0-9]+$/",$id)){
  8. $this->params["id"]=Decode($id);
  9. }
  10. }
  11. //自動解密ids
  12. if(isset($this->params["ids"]))
  13. {
  14. $ids=$this->params["ids"];
  15. if(!empty($ids)){
  16. $newids="";
  17. //print_r($idsarr);
  18. if(is_array($ids))
  19. foreach($ids as $key => $value){
  20. if(!preg_match("/^[0-9]+$/",$value)){
  21. if($newids!="")
  22. $newids.=','.Decode($value);
  23. else
  24. $newids=Decode($value);
  25. //$this->params["ids"]=
  26. }
  27. }
  28. else
  29. {
  30. if(!empty($ids)&&!preg_match("/^[0-9]+$/",$ids)){
  31. $newids=Decode($ids);
  32. }
  33. }
  34. $this->params["ids"]=$newids;
  35. }
  36. }
  37. }
 
這樣的話,傳到 咱們的控制器中的參數 便會自動解密了~~ 方便
 
至此,一個簡單的 MVC框架就完成了~~
 
後面有什麼要補的再補吧
相關文章
相關標籤/搜索