1. 程式人生 > >discuz程式碼解析(一、初始化應用的過程)

discuz程式碼解析(一、初始化應用的過程)

discuz是國內著名的論壇系統,今天大象有空看看原始碼,順便理下流程,跟哥交流請發郵件到[email protected]

一、首先必須知道的目錄結構

(一)uc_client Ucentent客戶端程式

(二)uc_server Ucentent服務端程式

(三)static 靜態檔案

(四)install 安裝目錄

(五)config 站點配置

(六)api 外部介面

(七)templets 模板目錄

(八)source 程式碼主目錄

(九)data 資料快取及附件

(十) archive 論壇靜態化

有圖有真相

二、流程結構(隨便找個地址來分析下,比如 設定 -> 個人資料 -> 聯絡方式;地址是:home.php?mod=spacecp&ac=profile&op=contact)

(一)首先開啟home.php檔案 看到第17行

1 require_once './source/class/class_core.php';

我同時打開了多個入口的檔案都有這麼一個引入,因此可以肯定這是個入口配置檔案,負責檔案引入及初始化需要的元件,下面來看下這個檔案 

第10行 error_reporting(E_ALL); 報告所有錯誤

第12-15行四個常量定義

1 2 3 4 define('IN_DISCUZ', true); define('DISCUZ_ROOT', substr(dirname(__FILE__), 0, -12)); define('DISCUZ_CORE_DEBUG'
, false); define('DISCUZ_TABLE_EXTENDABLE', TRUE);

第17-22行設定異常處理函式

1 2 3 4 5 6 set_exception_handler(array('core', 'handleException')); if(DISCUZ_CORE_DEBUG) { set_error_handler(array('core', 'handleError')); register_shutdown_function(array('core', 'handleShutdown')); }

第24-30行設定類自動引入的處理函式(這裡看仔細,很重要)

1 2 3 4 5 6 7 if(function_exists('spl_autoload_register')) { spl_autoload_register(array('core', 'autoload')); } else { function __autoload($class) { return core::autoload($class); } }

第32行 初始化core類

1 C::creatapp();

這個C是core類的對映,證據是第208行 

1 class C extends core {}

來看靜態方法creatapp() 

1 2 3 4 5 6 public static function creatapp() { if(!is_object(self::$_app)) { self::$_app = discuz_application::instance(); } return self::$_app; }

我們看到是返回了一個屬性,這個屬性_app 是discuz_application::instance()的返回值,我們來看discuz_application類,從上面的自動引入中我們看到這個類的地址是 ./source/class/discdz/discdz_application.php,

來看剛才執行的靜態方法 discuz_application::instance() 的返回值是個啥?

1 2 3 4 5 6 7 static function &instance() { static $object; if(empty($object)) { $object = new self(); } return $object; }

原來是按址引用的,並且是實理化自身了,這個類繼承自抽象類discuz_base,這個應該是個基類,來看看建構函式。

1 2 3 4 5 6 public function __construct() { $this->_init_env(); //274-316行;初始化環境變數。定義了些常量,引入了公共函式庫,在./source/function/function_core.php,設定最小記憶體128M。並設定了允許的全域性變數(_GET,_POST,_REQUEST,_COOKIE,_SERVER,_ENV,_FILES),定義了一個全域性變數$_G,變將這個全域性變數按址傳遞給了$this->var,看來在模板中可以直接使用這些變數的。 $this->_init_config();  //274-316行;初始化配置變數。引入了配置,在./config/config_global.php,將配置變數全部壓入到$this->var['config']中,在全域性都可以使用配置啦 $this->_init_input();//224-272行;初始化輸入。過濾了下全域性變數($_GET['GLOBALS'],$_POST['GLOBALS']……),如果開啟了get_magic_quotes_gpc(),就給$_GET,$_POST,$_COOKIE變數stripslashes一下,設定合法的cookie的鍵名必須 $this->config['cookie']['cookiepre']這個開頭的,在配置檔案中有。這樣做應該是防止非法cookie變數,243行把$_POST的值合併到$_GET變數中去。把rawurlencode($_GET['page'])編碼下,過濾了下$_GET['handlekey'],過濾$_GET變數,foreach($_GET as $k => $v) {$this->var['gp_'.$k] = daddslashes($v);}使用的是stripslashes函式。另外設定了幾個全域性變數 $this->var['XXX']自己瞅瞅。 $this->_init_output();//318-345行;初始化輸出。過濾URL$this->_xss_check();<"CONTENT-TRANSFER-ENCODING都是非法的。如果控制器為'seccode', 'secqaa', 'swfupload'則進行這個過濾,有時間看看./source/include/misc/misc_security.php;開啟ob_start(),設定charset; }

好了,現在我們明白了,我們現在得到了一個物件C,並且它有一個屬性叫_app,這個屬性是discuz_application物件,再在回到入口配置檔案class_core.php,下面執行到了第209行,我們看到一個對映

1 class DB extends discuz_database {}

DB代表了discuz_database物件,我們去看看這個物件。從上面的自動引入類機制中我們得到了這個類位於 ./source/class/discdz/discdz_application.php。開啟看看,原來是資料庫操作的;靜態方法 init($driver, $config)應該是初始化這個類的方法,從字面意思看,傳兩個引數進來,第一個引數是資料庫驅動,第二個引數是資料庫連線引數,以後的資料庫操作應該都是DB::update()  DB::delete這樣的方法了。

好了,跟著程式執行順序走,我們現在返回到入口檔案home.php第18行

1 require_once './source/function/function_home.php';

進去看看,原來是一組函式,這是個函式庫,瞄了兩眼,現在回到home.php第20行

1 $discuz = C::app();

原來是把discuz_application物件賦值給變數$discuz。

到第22-24行

1 2 3 $cachelist = array('magic','userapp','usergroups', 'diytemplatenamehome'); $discuz->cachelist = $cachelist; $discuz->init();

哇靠,$discuz->init();這一行才是核心中的核心,具體功能是初始化整個discuz應用。discuz_application類是整個discuz的應用初始化類,相當於織夢的 /include/common.inc.php的功能

1 2 3 4 5 6 7 8 9 10 11 12 public function init() { if(!$this->initated) { $this->_init_db();//初始化資料庫 $this->_init_setting();//系統設定初始化 $this->_init_user();//使用者資訊初始化 $this->_init_session();//session操作初始化 $this->_init_mobile();//手機功能初始化 $this->_init_cron();//計劃任務初始化 $this->_init_misc();//其他功能初始化 } $this->initated = true;//設定完成標誌 }

核心程式碼第一句 $this->_init_db();

1 2 3 4 5 6 7 8 9 private function _init_db() { if($this->init_db) { $driver = 'db_driver_mysql'; if(getglobal('config/db/slave')) {//在設定裡修改slave可以修改資料庫驅動類,預設為db_dirver_mysql $driver = 'db_driver_mysql_slave'; } DB::init($driver, $this->config['db']);//DB類位於:./source/class/discuz/discuz_database.php } }

DB::init方法是在discuz_database類中例項化了資料庫驅動類db_driver_mysql驅動類,並將其賦值給DB::db屬性,根據自動引入規則驅動類的位置在:./source/class/db/db_driver_mysql.php;

核心程式碼第二句 $this->_init_setting(),

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private function _init_setting() { if($this->init_setting) { if(empty($this->var['setting'])) { $this->cachelist[] = 'setting'; } if(empty($this->var['style'])) { $this->cachelist[] = 'style_default'; } if(!isset($this->var['cache']['cronnextrun'])) { $this->cachelist[] = 'cronnextrun'; } } !empty($this->cachelist) && loadcache($this->cachelist);//主要是這句,載入快取,實現的過程可以看function_core.php中的第687-716行,實際是把快取賦值給$_G變數($_G['setting'],$_G['cache'],$_G['grouplevels']),處理快取的其實是在第700行 C::t('common_syscache')->fetch_all($caches);,這塊是重點。可以研究一下,DZ的快取部分,這裡我略講一下,用C::t('common_syscache')->fetch_all($caches) 例項化了一個物件common_syscache 位置:./source/class/table/table_common_syscache.php,挺繞得,我看了半天程式碼才找出來,其實這個完整的物件是discuz_container。common_syscache物件只是discuz_container的一個屬性obj;在class_core.php的第76-90行可以看出這種關係來,table_common_syscache.php第38行,$data = memory('get', $cachenames);這個是最後得到的快取資料,我們來追下memory函式,第一個引數是get,那麼會執行function_core.php中的第1687行 case 'get': return C::memory()->get($key, $value); 其中C::memory()是例項化了discuz_memory物件,在./source/class/discuz/discuz_memory.php ;看get方法在第70-102行,它有兩個引數,get($key, $prefix = '')。get方法第97行$ret = $this->memory->get($this->_key($key));使用了memory_driver_eaccelerator類的get方法,位置./source/class/memory/memory_driver_eaccelerator.php第22行,return eaccelerator_get($key);,快取就是依靠eaccelerator_get函式獲取的 if(!is_array($this->var['setting'])) { $this->var['setting'] = array(); } }

核心程式碼第三句$this->_init_user();discuz_application.php 428-493行

$auth = getglobal('auth', 'cookie') 得到cookie中的auth的值,加密過的值

$auth = daddslashes(explode("\t", authcode($auth, 'DECODE'))); 利用authcode函式解密,得到一個數組

list($discuz_pw, $discuz_uid) = empty($auth) || count($auth) < 2 ? array('', '') : $auth; 從這句看陣列第一個元素為密碼,第二個元素是會員登陸的ID。存在common_member表中。

$user = getuserbyuid($discuz_uid, 1);利用getuserbyuid得到會員資訊,第二個引數如果是1並且在common_member中沒有該會員就去common_member_archive表中去尋找,否則直接去common_member表中查詢

1 2 3 4 5 6 7 8 9 if(!empty($user) && $user['password'] == $discuz_pw) {如果查出的密碼是對的 if(isset($user['_inarchive'])) { C::t('common_member_archive')->move_to_master($discuz_uid);如果陣列中有_inarchive元素就將當前查到的使用者資訊插入到使用者表common_member表中 } $this->var['member'] = $user;將使用者資訊壓入模板變數中 } else { $user = array(); $this->_init_guest();//設定沒登陸的訪客的資訊 }
1 2 3 if($user && $user['groupexpiry'] > 0 && $user['groupexpiry'] < TIMESTAMP && (getgpc('mod') != 'spacecp' || CURSCRIPT != 'home')) { dheader('location: home.php?mod=spacecp&ac=usergroup&do=expiry');//如果登陸時間過期了,或者訪問的是home.php下的內容時跳轉到登陸頁面 }

這裡作用是檢查登陸了沒有。

462行;!empty($this->cachelist) && loadcache($this->cachelist);裝載快取, 使用快取可以參照我另一篇文章中關於快取的應用

477行;$this->var['member']['lastvisit'] = TIMESTAMP - 3600;dsetcookie('lastvisit', TIMESTAMP - 3600, 86400 * 30);設定最後瀏覽的時間

464-490行,設定了cookie、全域性變數、增加var屬性變數

核心程式碼第四句 $this->_init_session();discuz_application.php 386-426行

設定session類,更新使用者狀態

1 2 3 4 5 <?php $sessionclose = !empty($this->var['setting']['sessionclose']);//設定中sessionclose是否為真 $this->session = $sessionclose ? new discuz_session_close() : new discuz_session();//如果為真就例項化discuz_session_close類 應該是關閉session後的解決方案,否則例項化discuz_session類,位置在source/class/discuz/discuz_session.php

 if($this->session->get('groupid') == 6)判斷session中的groupid合法性

 $this->session->set('lastactivity', TIMESTAMP);設定最後訪問時間為當前時間

 dsetcookie('lip', $this->var['member']['lastip'].','.$this->var['member']['lastvisit']);第一次訪問的話設定訪問IP的session

C::t('common_member_status')->update($this->var['uid'], array('lastip' => $this->var['clientip'], 'lastvisit' => TIMESTAMP));//更新使用者的狀態

?>

核心程式碼第五句 $this->_init_mobile();discuz_application.php 680-789行

大概看了下,都是設定手機訪問的一些變數,全域性變數,cookie,session,跳轉,常量等,有空細看下

核心程式碼第六句$this->_init_cron();discuz_application.php 508-515行

計劃任務初始化 。配置檔案中  $_config['remote']['cron'] = 1;設定後;遠端呼叫: 開啟外部 cron 任務. 系統內部不再執行cron, cron任務由外部程式啟用

1 2 3 4 5 6 $ext = empty($this->config['remote']['on']) || empty($this->config['remote']['cron']) || APPTYPEID == 200; if($this->init_cron && $this->init_setting && $ext) {//如果遠端呼叫: 開啟外部 cron 任務後呼叫discuz_cron::run();類 if($this->var['cache']['cronnextrun'] <= TIMESTAMP) { discuz_cron::run(); } }

至此我們看到了一個完整的初始化過程

1 2 3 4 5 6 7 require_once './source/class/class_core.php'; //引入核心類檔案,作用為:自動引入類規則,錯誤和異常處理,單例建立discuz_application類例項,引入預設函式庫function.core.php require_once './source/function/function_home.php';//專案函式庫 $discuz = C::app();//例項化desiuz_application類 $cachelist = array('magic','userapp','usergroups', 'diytemplatenamehome'); $discuz->cachelist = $cachelist;//設定快取列表 $discuz->init();//初始化整個應用

下一章將講到discuz控制器和模板

相關推薦

discuz程式碼解析初始應用過程

discuz是國內著名的論壇系統,今天大象有空看看原始碼,順便理下流程,跟哥交流請發郵件到[email protected] 一、首先必須知道的目錄結構 (一)uc_client Ucentent客戶端程式 (二)uc_server Ucentent服務端程式

無線資訊傳遞——Generic Netlink初始

系列說明   上一篇說明了無線資訊的user space至kernel space的大致傳遞流程,這一主要針對以下3點進行一個順序的描述: 1、無線驅動資訊傳遞框架:說明無線資訊傳遞的步驟流程以及各程式塊之間的聯絡; 2、generic Netlink訊號

30分鐘學會EventBus3.0詳解(一)(引入和初始化EventBus3.0 30分鐘學會EventBus3.0詳解)(EventBus3.0的詳細使用 30分鐘學會EventBus3.0詳解(一)(引入和初始化EventBus3.0 30分鐘學會EventBus3.0詳解)(Ev

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

結構體宣告初始記憶體對齊如何傳參

結構基礎知識聚合資料型別能夠同時儲存超過一個的單獨資料。C提供了兩種型別的聚合資料型別,分別是陣列和結構體。陣列是相同元素的集合,它的每個元素是通過下標引用或指標間接訪問的。結構體也是一些值的的集合,這些值稱為它 的成員,但一個結構的成員可能具有不同的型別。陣列元素可以通過下

oracle資料庫中重要的檔案控制檔案資料檔案重寫日誌檔案歸檔日誌檔案初始引數檔案

本篇著重從物理角度來說明oracle資料庫的組成。 Oracle資料庫,就是作業系統檔案的集合。包括下面幾種檔案。 1       控制檔案 引數檔案init.ora記錄了控制檔案的位置 控制檔案中的主要資訊: 資料庫的名字,檢查點資訊,資料庫建立的時間戳,所有的資料檔案

TFT LCD控制顯示總結硬體概念初始相關配置

01  void lcd_init(unsigned char pic_mode) 02  { 03      //配置LCD相關引腳 04      GPCUP = 0x00000000; 05      GPCCON = 0xaaaa02a9; 06      GPDUP = 0x00000000; 07

Node.js的學習入門module.exports與exports

user clas ret class script say req 引用 ole /*User.js*/ exports.userName = ‘Tom‘; exports.sayHello = function () { return ‘wwwwww‘ } /

jsp資料庫使用jdbc連線資料庫

步驟: 一、載入驅動程式 Class.forName("sun.jdbc.odbc.jdbcOdbcDriver"); 二、建立連線物件 Connection conn = DriverManager.getConnection("主機名","使用者名稱","密碼");

Hadoop學習記錄Hadoop叢集的搭建

參考:http://www.zuidemo.com/filePreview/pdfFilePreview/11202並進行補充 1.新建七個centos7系統的虛擬機器,分別命名為cluster1,cluster2等。關閉防火牆。 2.七臺主機都修改host檔案 vi /etc/host

從零開始之uboot移植uboot2017.01移植前的準備

手邊的是一個S5PV210的開發板,想嘗試移植一個比較新的uboot 下載最新版本uboot2018. 編譯器下載 交叉編譯工具鏈的安裝 1.在/usr/local/下面建立一個arm的資料夾,把交叉編譯工具解壓到下面去。 2.為了方便今後使用,

Vue.js零基礎學習筆記二章Vue介紹

作為一個iOS開發從業者,前段時間因為公司的需求剛剛學習並開發完成一個屬於前端開發工程師的任務-微信小程式(因為公司規模較小,人員配置不完全,十分無奈),深有感慨。對於移動端原生開發的iOS,Android的雙端開發,中小型公司為了節約開發成本,必然青睞於跨平臺

C/C++—— 在建構函式中呼叫虛擬函式能實現多型嗎Vptr指標初始過程分析

問題引入: 比如:如果我們想在父類的建構函式中呼叫虛擬函式,當定義子類物件的時候,父類的建構函式中的虛擬函式執行的是子類中的函式。 在下面的例子中,定義子類物件的時候,在父類建構函式中的print虛擬函式執行的不是子類中的print函式,而是父類中的prin

win10 tensorflow faster rcnn訓練自己的資料集製作VOC2007資料集

參考部落格:http://blog.csdn.net/gaohuazhao/article/details/60871886 一、關於VOC資料集: 1)JPEGImages資料夾 資料夾裡包含了訓練圖片和測試圖片,混放在一起 2)Annatations資料夾 資

Python 文字挖掘:使用機器學習方法進行情感分析特徵提取和選擇

def create_word_bigram_scores(): posdata = pickle.load(open('D:/code/sentiment_test/pos_review.pkl','r')) negdata = pickle.load(open('D:/code/senti

shell指令碼學習筆記shell指令碼變數語法

sh檔案用"#!"開頭表示用什麼來執行程式,如"#!/bin/bash" 表示用bin/bash來執行sh sh檔案,預設是文字檔案,是不可以執行的,可以通過chmod允許sh檔案可以被執行,chmod命令說明如下: chmod----改變一個或多個檔案的存取模式(mode) chmod

Android OpenGL ES2.0基礎最簡單的使用

一、OpenGL ES是什麼 OpenGL(Open Graphics Library)是一個跨程式語言、跨平臺的3D圖形庫。廣泛應用於遊戲、娛樂、VR等領域.安卓系統中的核心庫層就有這個。OpenGL ES是在OpenGL基礎上針對移動端而裁剪的 。Open

Java併發程式設計:volatile關鍵字解析.記憶體模型的相關概念

大家都知道,計算機在執行程式時,每條指令都是在CPU中執行的,而執行指令過程中,勢必涉及到資料的讀取和寫入。由於程式執行過程中的臨時資料是存放在主存(實體記憶體)當中的,這時就存在一個問題,由於CPU執行速度很快,而從記憶體讀取資料和向記憶體寫入資料的過程跟CPU執行指令

Servlet學習筆記之Servlet原理初始生命週期結構體系

Servlet是用java語言編寫的應用到Web伺服器端的擴充套件技術,與java物件的區別是,Servlet物件主要封裝了對HTTP請求的處理,並且它的執行需要Servlet容器的支援(以下會介紹原因,也可以看之前的一篇介紹Servlet容器的部落格,(http://blog.csdn.net/megust

JAVA虛擬機器JVM——類載入的過程載入驗證準備解析初始

載入 “載入”是”類載入”過程的一個階段。在載入階段,虛擬機器需要完成以下3件事情: 1.通過一個類的全限定名來獲取定義此類的二進位制位元組流。 2.將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。 3.在記憶體中生成一個代表這個類的java

JVM類載入的過程載入驗證準備解析初始

載入 載入一般可以分為顯示載入(通過Class.forName()方法載入類)和隱式載入(通過關鍵字new載入),這個過程主要完成三件事: 1.通過路徑/類全名獲取該類的class檔案的二進位制位元組流。 2.將存於class檔案中的靜態資料結構轉化成JVM方法區中執行