一、項目模塊規划
1、項目分為PC端、移動端、和PC管理端,分為對應目錄為 /Application/Home,/Application/Mobile,/Application/Admin;
對應入口文件為 index.php, mobile.php,admin.php,入口文件中設定綁定模塊;
ThinkPHP配置>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> index.php // 應用入口文件 // 檢測PHP環境 if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !'); // 開啟調試模式 建議開發階段開啟 部署階段注釋或者設為false define('APP_DEBUG',true); // 定義應用目錄 define('APP_PATH','./Application/'); // 綁定模塊 index.php define('BIND_MODULE','Home'); // 引入ThinkPHP入口文件 require './ThinkPHP/ThinkPHP.php'; // 親^_^ 后面不需要任何代碼了 就是如此簡單 //-------------------------------------------------- admin.php // 應用入口文件 // 檢測PHP環境 if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !'); // 開啟調試模式 建議開發階段開啟 部署階段注釋或者設為false define('APP_DEBUG',true); // 定義應用目錄 define('APP_PATH','./Application/'); // 綁定模塊 define('BIND_MODULE','Admin'); // 引入ThinkPHP入口文件 require './ThinkPHP/ThinkPHP.php'; // 親^_^ 后面不需要任何代碼了 就是如此簡單 //-------------------------------------------------- mobile.php // 應用入口文件 // 檢測PHP環境 if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !'); // 開啟調試模式 建議開發階段開啟 部署階段注釋或者設為false define('APP_DEBUG',true); // 定義應用目錄 define('APP_PATH','./Application/'); // 綁定模塊 define('BIND_MODULE','Mobile'); // 引入ThinkPHP入口文件 require './ThinkPHP/ThinkPHP.php'; // 親^_^ 后面不需要任何代碼了 就是如此簡單 //--------------------------------------------------
2、訪問的URL為 “域名+項目文件夾名+入口文件+控制器+方法”,如“localhost/myprj/index.php/Index/index”;
3、服務器配置域名綁定到項目文件夾,省略項目文件名,服務器上URL為“www.myprj.com/index.php/Index/index”;
<VirtualHost *:80> ServerName myprj.com RedirectMatch permanent ^/(.*) http://www.myprj.com/$1 </VirtualHost> <VirtualHost *:80> ServerName www.myprj.com DocumentRoot "/usr/local/apache/htdocs/myprj" </VirtualHost>
4、對於三個模塊的關系,我規划的是 PC端為父類,移動端和管理端均繼承於PC端;
二、配置和目錄規划
1、配置文件 /Application/Common/Conf/config.php為公共配置文件,用於配置數據庫信息、模板后綴名、自動開啟Session、URL模式等全項目公用的配置信息;
2、/Application/Home(或Mobile或Admin)/Conf/config.php為模塊配置文件,一般用於配置CSS、JS、圖片目錄,如下
<?php return array( //'配置項'=>'配置值' 'TMPL_PARSE_STRING'=>array( '__CSS__' => __ROOT__.'/Public/home/css', '__JS__' => __ROOT__.'/Public/home/js', '__IMG__' => __ROOT__.'/Public/home/image', '__PCSS__' => __ROOT__.'/Public/pub/css', '__PJS__' => __ROOT__.'/Public/pub/js', '__PIMG__' => __ROOT__.'/Public/pub/image', ) );
備注1:在CSS中引用圖片使用相對路徑,如 body { background
: url("../image/bgimage.png") }
備注2:模板在包含文件時要使用<include file="..." />標簽,使用<?php include '...'; ?>等原生PHP函數會導致包含文件中的__APP__、__JS__ 等預定義不被渲染;(框架BUG)
備注3:模板在包含公共模板文件時使用<include file="Index/header"/>,對應的公共模板文件路徑為 /View/Index/header.php ,此方法不經過控制器,所以不需要定義對應的方法,如果是其它控制器也不需要定義相對應的控制器。
3、/Application/Common/Common/function.php為公共函數文件,用於保存公共函數,如 密碼加密函數、表單過濾函數 等,這個文件會被自動調用不需要手動 require;
備注:為移植第三方接口(如微信支付、支付寶支付、OAuth登錄)修改工作較少,我把這些第三方DEMO放到了 /Application/Common/Common 目錄下,在function.php中編寫函數調用相關的接口類和函數。
4、設定模板文件的后綴名為php,因為一些IDE對html后綴的文件不能智能優化顯示其中的php代碼,比如Dreamweaver和Notepad++。
5、建議配置URL偽靜態后綴設為空(默認為html),以免在編程中生成帶參數的URL時出現異常的情況。(框架BUG)
6、如果TP3.2.3,作數據庫配置兼容處理(設計缺陷?)
//TP3.2.3兼容處理:列名返回時區分大小寫,原默認配置是全部為小寫 'DB_PARAMS'=>array(\PDO::ATTR_CASE => \PDO::CASE_NATURAL),
三、MVC划分
1、由於項目並不復雜,TP中提供了可不必定義的Model類,而如果定義Model類會在多模塊的繼承中增加復雜度,所以項目中均無定義Model類;可以看看一些開源項目中,不少Controller的方法只是對Model調用了一個方法然后ajax返回,非常冗余;
2、控制器分為兩大類,一類是專門負責模板渲染(assign和display),這里稱為模板控制器;另一類是負責數據庫操作和處理,這里稱為數據控制器;
3、為便於對於模板的統一控制,僅 Index 控制器為模板控制器;由於PC版有用戶中心一系列的模板,所以 UserCenter也是模板控制器;
4、原則上所有的數據庫操作不允許存在於模板控制器(如 Index控制器)中,應該寫在相應對象的數據控制器中;
5、同理原則上模板賦值(assign)和模板渲染(display)不允許存在於數據控制器中
6、Ajax返回寫在數據控制器中,對於同時支持被其它控制器和Ajax操作的方法,使用 $isReturn=FALSE 可選參數來決定輸出數據還是函數返回數據;
四、編程規范
1、文件、類、方法、函數命名規范參考Thinkphp官方規范
2、HTML/CSS、JS(jQuery)和PHP規范參考 這個鏈接>>
3、MySQL設計規范參考 這個鏈接>>
五、Thinkphp框架專用命名規范--團隊內部規范
1、類實例化成對象變量的命名
控制器命名的規則是 $+類名首字母小寫+字母C(表示控制器),即使只使用其中的一個方法也不要使用類中的方法名作為對象的名稱。
控制器命名的規則是 $+類名首字母小寫+字母M(表示模型),特別的空模型使用 $m,因為變量應該小寫字母開頭 。
$usrC = A('Usr'); $productC = A('Product'); $memberM = M('member'); $m = M(); 或者直接使用 M()->方法();
備注:實例化出來的類實例也是變量,變量名稱就要以小寫字母開頭;
2、數據變量的命名
雖然PHP的變量類型有好多,但在數據顯示方面,就基本上可以歸納為 字符串族 、一維數組族、多維數組族 這三種。
字符串族:整型、符點型、字符串,這一族可以直接使用 echo 或者類似Smarty的{$key} 等直接輸出;
一維數組族:這種一般是查詢數據庫得出來的只有一行數據(通常需要類似 $userInfo = $userInfoArr[0] 的小處理一下),這種一般是 assign 到模板然后用類似 {$userInfo['name']} 這種方式輸出;
多維數組族:這種一般是查詢數據庫得出來的多行數據,變量命名以Arr為后綴,如 $productArr = $productC->getIndexPro(); ,這種一般在模板用 foreach for 等循環遍歷出來;
六、部署:Linux下目錄權限設置及大小寫BUG
1)緩存目錄
項目/Application/Runtime/ 及其內目錄設置 777 權限
chmod 777 -R ./Application/Runtime/
如果仍不能正常生成緩存文件,檢查是否硬盤已滿。若系統為centos7,則要關閉 selinux 防火牆。
2)上傳目錄
項目/upload/ 設置 777 權限,注意目錄如果沒有可執行權限會導致 上傳時報類似“目錄不存在”這樣的錯誤。
chmod 777 ./upload/
上傳目錄內的所有文件都要設置成不可執行權限,這個似乎Linux沒有相關的配置,是在Apache或者.htaccess里面配置成不可執行PHP的,下面是.htaccess方式
#禁止上傳目錄 upload 的PHP執行權限,包括大小寫的PHP后綴 <FilesMatch "(?i:\.php)$"> Deny from all </FilesMatch>
3)項目應用目錄
所有的PHP訪問應該都應該從入口文件進入,CSS/JS/圖片等可以不必經過入口文件。那么就應該屏蔽整個代碼項目的文件的直接訪問,而不只是TP官方文檔所說的只是保護模板文件,所以直接在 項目/Application/ 目錄下放置一個 .htaccess 文件,寫上下面的內容
#項目目錄屏蔽所有沒經過入口文件,直接URL訪問的 <FilesMatch "(.*)"> Deny from all </FilesMatch>
4)關閉調試模式
把服務器上的index.php、admin.php等入口文件注釋掉 define('APP_DEBUG',true); 即關閉調試模式,注意不要再上傳到SVN,本地開發仍然使用調試模式。關閉調試模式要在TP的配置文件 項目/Application/Common/Config/config.php 里加上(框架BUG)
'URL_CASE_INSENSITIVE' => FALSE, //調試時是false的//部署時是true會導致Linux下模板渲染文件名全部轉換為小寫字母而出錯!!
5)緩存清理
關閉調試模式后,會生成配置緩存文件。每次更改配置文件都要刪除 項目/Application/Runtime/common~runtime.php 文件才能使新配置生效;(文檔BUG)
更改配置后頁面顯示不正常,要清理頁面緩存,清空 項目/Application/Runtime/Cache 目錄里面的文件;
注意不能把 項目/Application/Runtime 整個目錄刪除,它不會自動生成,會導致無法生成各種緩存而使程序無法正常執行;(框架BUG)
管理后台可以加入一個“清理緩存”的按鈕

//清理緩存 function clearCache(){ $cacheDir = $_SERVER['DOCUMENT_ROOT'].__ROOT__.'/Application/Runtime' ; // var_dump( $cacheDir ); $ok = deldir( $cacheDir ); // var_dump($ok); //增加回緩存目錄,並設置權限為777 mkdir( $cacheDir ); chmod( $cacheDir, 0777); echo $ok; } //刪除整個目錄 function deldir($dir) { //先刪除目錄下的文件: $dh=opendir($dir); while ($file=readdir($dh)) { if($file!="." && $file!="..") { $fullpath=$dir."/".$file; if(!is_dir($fullpath)) { unlink($fullpath); //var_dump($fullpath); } else { deldir($fullpath); } } } closedir($dh); //刪除當前文件夾: if(rmdir($dir)) { return true; } else { return false; } }
七、URL優化和重寫
服務器上部署還可以啟用TP的“REWRITE模式”,同時apache配置相應的域名對相應的入口文件,如 www.prj.com 到 index.php ,m.prj.com 到 mobile.php ,admin.prj.com 到 admin.php ,URL進一步縮寫省去入口文件,如“www.myprj.com/Index/index”。
1)Apache配置,不同的域名設置不同的首頁文件
<VirtualHost *:80>
DocumentRoot "D:\wamp\www\batsing" ServerName www.batsing.com DirectoryIndex index.php </VirtualHost> <VirtualHost *:80> DocumentRoot "D:\wamp\www\batsing" ServerName m.batsing.com DirectoryIndex mobile.php </VirtualHost> <VirtualHost *:80> DocumentRoot "D:\wamp\www\batsing" ServerName admin.batsing.com DirectoryIndex admin.php </VirtualHost>
2) .htaccess文件配置(Apache專用,先開啟rewrite_module)
<IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine On #設置用戶端的重寫規則,入口文件index.php,隱藏index.php RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{HTTP_HOST} ^www.batsing.com$ [NC] RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L] #設置移動端的重寫規則,入口文件mobile.php,隱藏mobile.php RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{HTTP_HOST} ^m.batsing.com$ [NC] RewriteRule ^(.*)$ mobile.php?/$1 [QSA,PT,L] #設置管理端的重寫規則,入口文件admin.php,隱藏admin.php RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{HTTP_HOST} ^admin.batsing.com$ [NC] RewriteRule ^(.*)$ admin.php?/$1 [QSA,PT,L] #404頁重定向,框架外 ErrorDocument 404 /notfound.html #測試,指定瀏覽器 重定向URL (自動從www重定向到mobile) # RewriteCond %{HTTP_HOST} ^www.batsing.com$ [NC] # RewriteCond %{HTTP_USER_AGENT} "Mobile" [NC] #含Mobile字眼的瀏覽器(包括微信、UC移動、QQ移動、Safari移動、小米原生) # RewriteRule ^(.*)$ http://m.batsing.com/ [R=301,NC,L] #這一段放到httpd.conf去了,不用每次都讀取.htaccess文件
#注意,如果apache 與 PHP 是以fast-cgi的方式運行,
#那么 RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L] 需要修改為 RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1]
#否則會出現 No input file specified. 的錯誤。
</IfModule>
3) ThinkPHP的項目公共配置文件 /Application/Common/Config/config.php 增加一行開啟URL訪問模式為 2,默認為模式1
'URL_MODEL' => 2, // URL訪問模式,可選參數0、1、2、3
注解:設置URL模式是為了讓系統生成的鏈接(如__APP__,{:U('xxx')} 等)不再包含index.php這一串,即使不修改thinkphp的url模式,也可以通過不帶index.php的方式訪問網頁。
4)本地測試辦法,修改hosts文件,配置相關的域名到本地
127.0.0.1 www.batsing.com
127.0.0.1 m.batsing.com 127.0.0.1 admin.batsing.com
1+2.)Nginx的配置URL重寫
server { listen 80; server_name www.batsing.com ; root "D:\wamp\www\batsing"; location / { index index.php; #域名對應的首頁(對應Apache的DirectoryIndex) #autoindex on; if (!-e $request_filename){ #index.php 縮寫,與上面apache的.htaccess的功能一樣 rewrite ^/(.*)$ /index.php?/$1; } } location ~ \.php(.*)$ { #開啟了pathinfo的fastcgi模式的PHP fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_split_path_info ^((?U).+\.php)(/?.+)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; include fastcgi_params; } }
上面配置中的 if 和左括號間要有空格,否則報錯無法啟動nginx
!注意:開啟了pathinfo功能的fastcgi模式的php存在文件類型錯誤解析漏洞 。注意上傳目錄和靜態資源目錄(css/js)的安全
八、服務器環境和本地環境不同配置
服務上關閉調試模式,本地開啟調試模式。所以服務器上只會加載 config.php ,而本地還會加載 debug.php並替代config.php中的配置項。總結所寫的配置如下:
config.php <?php return array( //'配置項'=>'配置值' 'DB_TYPE'=>'mysqli', // 數據庫類型 'DB_HOST'=>'localhost', // 服務器地址 'DB_NAME'=>'dbname', // 數據庫名 'DB_USER'=>'dbuser1', // 用戶名 'DB_PWD'=>'dbpwd1', // 密碼 'DB_PORT'=>3306, // 端口 'DB_PREFIX'=>'', // 數據庫表前綴 'DB_CHARSET'=>'utf8', // 數據庫字符集 'TMPL_TEMPLATE_SUFFIX' => '.php', //模板后綴名為php 'URL_HTML_SUFFIX' => '', //偽靜態success、error、redirect、U()生成的URL后綴為空 'URL_MODEL' => 2, // URL訪問模式,可選參數0、1、2、3 'URL_CASE_INSENSITIVE' => FALSE, //調試時是false的//部署時是true會導致Linux下模板渲染文件名全部轉換為小寫字母而出錯!! //TP3.2.3兼容處理:列名返回時區分大小寫,原默認配置是全部為小寫 'DB_PARAMS'=>array(\PDO::ATTR_CASE => \PDO::CASE_NATURAL), ); debug.php <?php //數據庫配置信息 return array( //'配置項'=>'配置值' 'DB_USER'=>'root', // 用戶名 'DB_PWD'=>'localdbpwd', // 密碼 'URL_MODEL' => 1, // URL訪問模式,默認1,本地無配置域名 'SHOW_PAGE_TRACE'=>true, //開啟頁面Trace );
九、手機瀏覽器自動從PC版跳轉到移動版
參考我的這篇博文 Apache配置必配基礎>>
十、一些shell腳本
2)上傳代碼后設置目錄的可讀權限;
3)清空緩存目錄;
4)所有非上傳目錄、緩存目錄、日志目錄 一鍵加鎖不可寫,以及一鍵解鎖為可寫;
十一、其它一些BUG
1)success、error方法跳轉不正常,比如 UserController里面的 login方法成功后跳轉到 IndexController里面的 center方法,使用 $this->success('登錄成功', 'Index/center'); 是會生成 Index/center ,瀏覽器就識別跳轉到了 /User/Index/center,導致404,而不是 /Index/center 的。解決方法是改成 $this->success('登錄成功', U('Index/center'));
原因是這兩個方法是直接在提示頁面生成 <a href="Index/center">正在跳轉</a> 這樣的HTML,這樣則會使跳轉錯誤。而后面一種則是生成一個完整(但不含域名)的URL。
2)調用方法時頁面渲染不符合預期,像下面,本來預期 do() 方法進入if條件調用 doA() 后會渲染 doA 頁面,但結果卻是渲染 do 頁面(沒有do頁面則報錯)。查看源碼可以發現它的渲染規則是,渲染的頁面默認是跟請求URL所對應的控制器和Action的。
<?php class IndexController { function do(){ if( ... ){ $this->doA(); return; }else{ ... } } function doA(){ ... $this->display(); //渲染 doA 頁面 } }
3)引入的CSS和JS不會經過模板渲染。所有在CSS、JS中都無法使用 __APP__、{:U('Ctrl/method')} 等方法。CSS引用圖片建議使用相對路徑,如 background: url("./img/xxx.jpg");
4)統一過濾的 I() 方法用的函數 htmlspecialchars 沒有轉義單引號 ' ,可能會造成 XSS 漏洞,最好自己定義一個更加強大、安全的過濾函數。如:
htmlspecialchars(trim($data), ENT_QUOTES)
5)GET或POST中的參數名為m、c、a 時,都會出現路由錯誤,其URL模式為普通模式時這是可以理解的,但是其它的URL模式都有這樣的問題,而且連POST也會這樣處理,只能說是BUG了。可以修改配置如下一般能達到避免效果
//修改URL中的獲取變量的名字 'VAR_MODULE' => '__m__', // 模塊獲取變量 'VAR_CONTROLLER' => '__c__', // 控制器獲取變量 'VAR_ACTION' => '__a__', // 操作獲取變量 'VAR_PATHINFO' => '__s__', // 操作獲取變量
∞、靜態化
利用URL重寫規則,判斷靜態文件是否存在,存在則直接顯示,否則定向到TP框架中處理;
覆蓋重寫TP中的display()方法,讓其除了生成頁面外,還生成靜態頁面;
需要靜態化的頁面在顯示如用戶名等通用信息時使用ajax獲取;
具體配置和方法以后貼出,敬請期待。