- <?php?
- /*?
- April?18,2012?
- discuz二次開發學習?
- author:xuqin?
- 不能為了完成任務去做一件事,要舉一反三,融會貫通的去學習。?
- */?
- error_reporting(0);?
- /*?
- ?*?error_reporting(0);?//抑制所有的出錯信息?
- ?*?error_reporting(E_ALL);//顯示所有的出錯信息?
- ?*/?
- ?
- set_magic_quotes_runtime(0);?
- /*?
- ?*?set_magic_quotes_runtime(0);?//關閉魔法引用?
- ?*?set_magic_quotes_runtime(1);?//開啟魔法引用?
- ?*?什么事魔術引號??
- ?*?當打開時,所有的?'(單引號),"(雙引號),\(反斜線)和?NULL?字符都會被自動加上一個反斜線進行轉義。?
- ?*?這和?addslashes()?作用完全相同。?
- ?*?一共有三個魔術引號指令:?
- ?*?magic_quotes_gpc?影響到?HTTP?請求數據(GET,POST?和?COOKIE)。不能在運行時改變。在?PHP?中默認值為?on。?參見?get_magic_quotes_gpc()。?
- ?*?magic_quotes_runtime?如果打開的話,大部份從外部來源取得數據并返回的函數,包括從數據庫和文本文件,所返回的數據都會被反斜線轉義。該選項可在運行的時改變,在?PHP?中的默認值為?off。?參見?set_magic_quotes_runtime()?和?get_magic_quotes_runtime()。?
- ?*?magic_quotes_sybase?如果打開的話,將會使用單引號對單引號進行轉義而非反斜線。此選項會完全覆蓋?magic_quotes_gpc。如果同時打開兩個選項的話,單引號將會被轉義成?''。而雙引號、反斜線?和?NULL?字符將不會進行轉義。?如何取得其值參見?ini_get()。?
- ?*/?
- ?
- $mtime?=?explode('?',?microtime());?
- /*?
- ?*??microtime()取得Unix?時間戳和微秒數后使用explode()進行切割并存進數組變量$mtime;?
- ?*????eg:?
- ?*????echo?microtime();?//將顯示?0.29353300?1164349567?(根據時間的不同會顯示不同的數字,但格式是一樣的?
- ?*????$mtime[0]="0.29353300";?
- ?*????$mtime[1]="1164349567";?
- ?*/?
- $discuz_starttime?=?$mtime[1]?+?$mtime[0];//得到腳本開始運行的時間?
- ?
- define('SYS_DEBUG',?FALSE);//關閉debug模式?
- /*?
- ?*?編譯方式的本質區別?Debug?通常稱為調試版本,它包含調試信息,并且不作任何優化,便于程序員調試程序。Release?稱為發布版本,它往往是進行了各種優化,使得程序在代碼大小和運行速度上都是最優的,以便用戶很好地使用。?
- ?*/?
- define('IN_DISCUZ',?TRUE);//用于防止非法引用,定義一個常量表示只有DISCUZ才可以使用本腳本?
- define('DISCUZ_ROOT',?substr(dirname(__FILE__),?0,?-7));//定義常量DISCUZ_ROOT的值(即bbs的路徑)?
- define('MAGIC_QUOTES_GPC',?get_magic_quotes_gpc());//將魔術引號開啟狀態存入常量?
- !defined('CURSCRIPT')?&&?define('CURSCRIPT',?'');//這個寫法很特別哦!!初始化當前運行的腳本名稱為空,CURSCRIPT這個常量在很多頁面都有,意思是如果沒定義CURSCRIPT常量,?則定義CURSCRIPT為空。?
- ?
- if(PHP_VERSION?<?'4.1.0')?{//php舊版本超全局變量兼容處理,注意是引用賦值"&$"?php基礎知識哦!去復習下吧!?
- ????$_GET?=?&$HTTP_GET_VARS;?
- ????$_POST?=?&$HTTP_POST_VARS;?
- ????$_COOKIE?=?&$HTTP_COOKIE_VARS;?
- ????$_SERVER?=?&$HTTP_SERVER_VARS;?
- ????$_ENV?=?&$HTTP_ENV_VARS;?
- ????$_FILES?=?&$HTTP_POST_FILES;?
- }?
- /*?
- ?*??作用:當PHP版本小于4.1.0?的時候使用傳址方式獲取內置全局變量?
- ?*????示例:?
- ?*????為什么要使用傳址而不是傳值方式??
- ?*????要點:在變量前加?&?符號既是傳址?
- ?*????<?php?
- ?*????$a=1;?
- ?*????$b=$a;?
- ?*????$b++;?
- ?*????echo?"b=".$b."?a=".$a;?//顯示:b=2?a=1?可以看到?b變了,而a?卻還是沒變?
- ?*????
- ?*????echo?"<br>";?
- ?*????$c=1;?
- ?*????$d=&$c;??//注意這行??&符號?
- ?*????$d++;?
- ?*????echo?"d=".$d."?c=".$c;?//顯示:?d=2?c=2?可以看到d變了,可是c也變了?
- ?*?????>?
- ?*/?
- ?
- if?(isset($_REQUEST['GLOBALS'])?OR?isset($_FILES['GLOBALS']))?{?
- ????exit('Request?tainting?attempted.');?
- }?
- /*?
- ?*??眾所周知,當php.ini里面的register_globals=on時,各種變量都被注入代碼,例如來自?HTML?表單的請求變量。再加上?PHP?在使用變量之前是無需進行初始化的。那么就有可能導致不安全,假如有人惡意發出這么一個get請求"http://yourdomain?/unsafe.php?GLOBALS=",那么就會清除$GLOBALS變量的值而導致不安全。?
- ?*?
- ?*??register_globals是php.ini里的一個配置,這個配置影響到php如何接收傳遞過來的參數,如果你的問題是:為什么我的表單無法傳遞數據?為什么我的程序無法得到傳遞過來的變量?等等,那么你需要仔細的閱讀以下的內容。?
- ?*????register_globals的值可以設置為:On或者Off,我們舉一段代碼來分別描述它們的不同。?
- ?*????代碼:????
- ?*????<form?name="frmTest"?id="frmTest"?action="URL">?
- ?*????<input?type="text"?name="user_name"?id="user_name">?
- ?*????<input?type="password"?name="user_pass"?id="user_pass">?
- ?*????<input?type="submit"?value="login">?
- ?*????</form>????
- ?*????當register_globals=Off的時候,下一個程序接收的時候應該用$_GET['user_name']?和$_GET['user_pass']來接受傳遞過來的值。(注:當<form>的method屬性為post的時候應該用$_POST['user_name']和$_POST['user_pass'])?
- ?*????當register_globals=On的時候,下一個程序可以直接使用$user_name和$user_pass來接受值。?
- ?*????顧名思義,register_globals的意思就是注冊為全局變量,所以當On的時候,傳遞過來的值會被直接的注冊為全局變量直接使用,而Off的時候,我們需要到特定的數組里去得到它。?
- ?*?
- ?*/?
- ?
- require_once?DISCUZ_ROOT.'./include/global.func.php';//引入全局函數庫,很重要?
- /*?
- ?*??這里復習下:require;require_once();include();incdlue_once()區別?
- ?*?require()?
- ?*include()?
- ?*這兩一般放在代碼前面,功能除了處理失敗情況不一樣,其它都是一樣,如果包含的文件不存在時,require會停止運行發生致命錯誤提示。而include則是只顯示一警告,代碼會繼續執行。?
- ?*require_once()?
- ?*include_once()?
- ?*這兩一個一般放在流程控制中,除了以上錯誤處理的區別外,功能都是一樣的,跟沒有once的區別就是包含的文件已經在前包含過,第二次包含不會再被引入。?
- ?*/?
- define('IS_ROBOT',?getrobot());//定義IS_ROBOT常量,判斷瀏覽此頁面的user-agent是否為搜索引擎蜘蛛,getrobot函數見global.func.php?
- if(defined('NOROBOT')?&&?IS_ROBOT)?{//如果定義了此頁不能被機器人找到,但是發現訪問者是搜索引擎蜘蛛,則返回禁止信息。?
- ????exit(header("HTTP/1.1?403?Forbidden"));?
- }?
- ?
- foreach(array('_COOKIE',?'_POST',?'_GET')?as?$_request)?{//過濾提交的變量,提高安全性?
- ????foreach($$_request?as?$_key?=>?$_value)?{?
- ????????$_key{0}?!=?'_'?&&?$$_key?=?daddslashes($_value);//變量名第一個字母不能為下劃線,防止有類似偽造的"$_POST"變量產生?
- ????}?
- }?
- /*?
- ?*?addslashes()?函數在指定的預定義字符前添加反斜杠。?
- ?*?[單引號?(')雙引號?(")反斜杠?(\)NULL]默認情況下,PHP?指令?magic_quotes_gpc?為?on,對所有的?GET、POST?和?COOKIE?數據自動運行?addslashes()。不要對已經被?magic_quotes_gpc?轉義過的字符串使用?addslashes(),因為這樣會導致雙層轉義。遇到這種情況時可以使用函數?get_magic_quotes_gpc()?進行檢測。?
- ?*?stripcslashes()?函數?刪除?由?addcslashes()?函數添加的反斜杠。(也可刪除服務器開啟magic_quotes_gpc后自動添加上的反斜杠)?
- ?*/?
- ?
- if?(!MAGIC_QUOTES_GPC?&&?$_FILES)?{//轉義$_FILES變量,注意這里先要判斷MAGIC_QUOTES_GPC常量的狀態,如果是關閉的才進行轉義,否則會造成多次轉義?
- ????$_FILES?=?daddslashes($_FILES);?
- }?
- ?
- $charset?=?$dbcharset?=?$forumfounders?=?$metakeywords?=?$extrahead?=?$seodescription?=?'';?
- //全局變量初始化:初始化文件字符集,數據庫字符集,版塊創建者,SEO的論壇關鍵字、描述,頭部其他信息等變量?
- $plugins?=?$hooks?=?$admincp?=?$jsmenu?=?$forum?=?$thread?=?$language?=?$actioncode?=?$modactioncode?=?$lang?=?array();?
- /*?
- ?*?初始化插件,鉤子,后臺管理,js彈出菜單,版塊,帖子,語言,動作代碼,系統提示信息等數組?
- ?*?[鉤子]實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達前臺目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。?
- ?*/?
- require_once?DISCUZ_ROOT.'./config.inc.php';//引入系統配置文件,內含如數據庫連接信息、cookie域、UC信息等?
- ?
- $_DCOOKIE?=?$_DSESSION?=?$_DCACHE?=?$_DPLUGIN?=?$advlist?=?array();//初始化cookie,session,緩存,插件,廣告列表等數組?
- ?
- $prelength?=?strlen($cookiepre);//定義(獲取)COOKIE前綴長度$prelength為$cookiepre的長度(見config.inc)?
- ?
- foreach($_COOKIE?as?$key?=>?$val)?{?
- ????if(substr($key,?0,?$prelength)?==?$cookiepre)?{?
- ????????$_DCOOKIE[(substr($key,?$prelength))]?=?MAGIC_QUOTES_GPC???$val?:?daddslashes($val);?
- ????}?
- }?
- /*?
- ?*?從瀏覽器$_COOKIE得到$_DCOOKIE數組:如果cookie的前綴與系統配置的cookie前綴相同,則填入各cookie的值?
- ????????這里$_DCOOKIE各變量的名稱是不包含前綴的(例如$_COOKIE['tmp_cook1']變成了$_DCOOKIE['cook1']),同時對值進行轉義?
- ?*/?
- unset($prelength,?$_request,?$_key,?$_value);?
- /*?
- ?*?上面對COOKIE遍歷完成后,便立即銷毀$prelength等相關變量。?
- ?*?清空一些無用變量,如臨時的$_POST、$_GET等(因為已經在本文件前面處理中得到了很多的單獨變量)?
- ?*/?
- $inajax?=?!emptyempty($inajax);//先定義一個備用的變量吧,與ajax有關嗎??
- $timestamp?=?time();//獲得一個當前的時間戳變量,如:1334714617,與前面到microtime不同,time()只會得到毫秒數?
- ?
- if($attackevasive?&&?CURSCRIPT?!=?'seccode')?{?
- ????require_once?DISCUZ_ROOT.'./include/security.inc.php';//防御文件,根據$attackevasive設定的不同級別做相應處理。?
- }?
- /*?
- ?*?是否啟用攻擊防御。$attackevasive默認為0,關閉論壇防御。如果配置文件config.inc定義了論壇防御級別,且當前腳本文件名不是seccode(驗證碼?),則引入相關防御的文件?
- ?*/?
- ?
- require_once?DISCUZ_ROOT.'./include/db_'.$database.'.class.php';//引入數據庫處理類?
- ?
- $PHP_SELF?=?$_SERVER['PHP_SELF']???$_SERVER['PHP_SELF']?:?$_SERVER['SCRIPT_NAME'];//獲得當前執行腳本的文件相對路徑?
- $BASESCRIPT?=?basename($PHP_SELF);//返回路徑的文件名部分?
- $boardurl?=?htmlspecialchars('http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api|archiver|wap)?\/*$/i",?'',?substr($PHP_SELF,?0,?strrpos($PHP_SELF,?'/'))).'/');?
- /*?
- ?*?取當前url?返回bbs的url(htmlspecialchars將特殊字符”<>“等轉換為”<?$gt“等)仔細看下substr、strpos那段,看這樣是什么效果?
- ?*?strrpos()?函數查找字符串在另一個字符串中最后一次出現的位置?
- ?*?preg_replace()?執行一個正則表達式的搜索和替換?
- ?*/?
- ?
- if(getenv('HTTP_CLIENT_IP')?&&?strcasecmp(getenv('HTTP_CLIENT_IP'),?'unknown'))?{?
- ????$onlineip?=?getenv('HTTP_CLIENT_IP');?
- }?elseif(getenv('HTTP_X_FORWARDED_FOR')?&&?strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),?'unknown'))?{?
- ????$onlineip?=?getenv('HTTP_X_FORWARDED_FOR');?
- }?elseif(getenv('REMOTE_ADDR')?&&?strcasecmp(getenv('REMOTE_ADDR'),?'unknown'))?{?
- ????$onlineip?=?getenv('REMOTE_ADDR');?
- }?elseif(isset($_SERVER['REMOTE_ADDR'])?&&?$_SERVER['REMOTE_ADDR']?&&?strcasecmp($_SERVER['REMOTE_ADDR'],?'unknown'))?{?
- ????$onlineip?=?$_SERVER['REMOTE_ADDR'];?
- }?
- /*?
- ?*?獲得用戶到ip地址,存入$onlineip變量?
- ?*?注意PHP三個獲取ip的系統變量。?
- ?*?HTTP_CLIENT_IP?是tcp/ip協議里得到的ip地址,apache收到請求并返回就是要返回到這個ip去。?
- ?*?HTTP_X_FORWARDED_FOR?要想透過代理服務器取得客戶端的真實?IP?地址。?
- ?*?REMOTE_ADDR?取得客戶端的?IP地址,如果客戶端是使用代理服務器來訪問,那取到的就是代理服務器的IP地址。?
- ?*/?
- ?
- preg_match("/[\d\.]{7,15}/",?$onlineip,?$onlineipmatches);//使用正則匹配函數得到符合格式的ip地址,看看ip是不是點分段,7-15個數字之間?
- $onlineip?=?$onlineipmatches[0]???$onlineipmatches[0]?:?'unknown';//如果上面獲取到符合ip格式的ip就賦值給$onlineip?
- unset($onlineipmatches);//立即清空?IP檢查變量,方便下次處理?
- ?
- $cachelost?=?(@include?DISCUZ_ROOT.'./forumdata/cache/cache_settings.php')???''?:?'settings';?
- @extract($_DCACHE['settings']);?
- /*?
- ?*?讀取論壇設置的緩存文件cache_settings.php?
- ?*?如果能夠正常讀到,cachelost為空,既沒有lost(丟失),這一步得到$_DCACHE['settings']數組(很重要,很多變量以后都要用到)?
- ?*?這一段是獲得./forumdata/cache/cache_settings.php(即緩存下的設置數組,并展開,方便以后的寫法)?
- ?*?將setting數組變量導入符號表,成為獨立變量的形式,前面循環$$_key?=?$value處理$_POST等也是這個原理吧?
- ?*/?
- ?
- if($gzipcompress?&&?function_exists('ob_gzhandler')?&&?!in_array(CURSCRIPT,?array('attachment',?'wap'))?&&?!$inajax)?{?
- ????ob_start('ob_gzhandler');?
- }?else?{?
- ????$gzipcompress?=?0;?
- ????ob_start();//輸出緩存(開啟輸出緩沖)?
- }?
- /*?
- ?*?$gzipcompress(是否啟用gzip)此變量來自@extract($_DCACHE['settings'])?
- ?*?檢查gzip是不是打開了,打開就用ob_gzhandler,沒有就用ob_start。?
- ?*?如果開啟了gzip壓縮,并且ob_gzhandler函數存在,并且當前腳本不是附件和wap,并且不是ajax,則開始使用ob_gzhandler壓縮,否則只進行輸出緩存而不壓縮?
- ?*/?
- ?
- if(!emptyempty($loadctrl)?&&?substr(PHP_OS,?0,?3)?!=?'WIN')?{?
- ????if($fp?=?@fopen('/proc/loadavg',?'r'))?{?
- ????????list($loadaverage)?=?explode('?',?fread($fp,?6));?
- ????????fclose($fp);?
- ????????if($loadaverage?>?$loadctrl)?{?
- ????????????header("HTTP/1.0?503?Service?Unavailable");?
- ????????????include?DISCUZ_ROOT.'./include/serverbusy.htm';?
- ????????????exit();?
- ????????}?
- ????}?
- }?
- /*?
- ?*?此段為在linux平臺下處理負載平衡?
- ?*?對于非win平臺,進行服務器負載判斷并進行操作。$loadctrl為負載臨界值。?
- ?*/?
- ?
- if(in_array(CURSCRIPT,?array('index',?'forumdisplay',?'viewthread',?'post',?'topicadmin',?'register',?'archiver')))?{?
- ????$cachelost?.=?(@include?DISCUZ_ROOT.'./forumdata/cache/cache_'.CURSCRIPT.'.php')???''?:?'?'.CURSCRIPT;?
- }?
- /*?
- ?*?繼續判斷當前dz緩存存在的狀況,看看index,?forumdisplay,?viewthread這些文件是不是緩存了,有的話把它裝到$cachelost這個變量中。?
- ?*?對不同的腳本頁面,引入不同的緩存文件?
- ?*/?
- ?
- $db?=?new?dbstuff;?
- $db->connect($dbhost,?$dbuser,?$dbpw,?$dbname,?$pconnect,?true,?$dbcharset);?
- $dbuser?=?$dbpw?=?$dbname?=?$pconnect?=?NULL;?
- /*?
- ?*?這里是初始化一個dbstull類的實例,也就是說前面的require_once?DISCUZ_ROOT.'./include/db_'.$database.'.class.php';在這里用上了?
- ?*?然后連接數據庫并清理相關變量。由此可見DZ的安全性考慮真是牛到不行了。小氣鬼!?呵呵!?
- ?*/?
- ?
- $sid?=?daddslashes(($transsidstatus?||?CURSCRIPT?==?'wap')?&&?(isset($_GET['sid'])?||?isset($_POST['sid']))???
- ????(isset($_GET['sid'])???$_GET['sid']?:?$_POST['sid'])?:?
- ????(isset($_DCOOKIE['sid'])???$_DCOOKIE['sid']?:?''));?
- /*?
- ?*?這里是個很長的三元運算符哦!對自定義的回話標識符進行過濾?
- ?*?開啟sid后,即使禁用cookie也可以登陸論壇$transsidstatus也是extract($_DCACHE['setting'])得到的,所以,熟悉cache_setting里面的變量才不會經常一頭霧水?
- ?*?如果開啟了$sid且是wap,且從GET或POST傳遞來$sid,則從GET或者POST中取得,否則從cookie取得?
- ?*/?
- ?
- $discuz_auth_key?=?md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);?
- /*?定義驗證字符串,驗證鑰匙。'authkey'?=>?'0ee545SPQKK3jOP2'也是extract($_DCACHE['setting'])定義的。?
- ?*?$_SERVER["HTTP_USER_AGENT"]獲取客戶端瀏覽器的型號,這個不是一定準確的,是可以隨意偽造的?
- ?*/?
- ?
- list($discuz_pw,?$discuz_secques,?$discuz_uid)?=?emptyempty($_DCOOKIE['auth'])???array('',?'',?0)?:?daddslashes(explode("\t",?authcode($_DCOOKIE['auth'],?'DECODE')),?1);?
- /*?
- ?*?初始化變量:密碼,安全問題,用戶id,如果$_DCOOKIE['auth']為空,則初始化他們,否則解碼并讀取相應變量。?
- ?*?對常用變量進行賦值。$discuz_secques為用戶按安全驗證回答的哈希值。authcode()根據$authkey對輸入參數進行加密解密的操作。?
- ?*?這一段是用來檢查是不是$_DCOOKIE[‘auth’]存在,如果有的話就把其中存放的東西分別給$discuz_pw,?$discuz_secques,?$discuz_uid這三個變量,分別對應密碼,提示問題和uid。?
- ?*/?
- ?
- $newpm?=?$newpmexists?=?$sessionexists?=?$seccode?=?0;//初始化用戶的新消息,安全問題,驗證碼等變量?
- ?
- $membertablefields?=?'m.uid?AS?discuz_uid,?m.username?AS?discuz_user,?m.password?AS?discuz_pw,?m.secques?AS?discuz_secques,?
- ????m.adminid,?m.groupid,?m.groupexpiry,?m.extgroupids,?m.email,?m.timeoffset,?m.tpp,?m.ppp,?m.posts,?m.digestposts,?
- ????m.oltime,?m.pageviews,?m.credits,?m.extcredits1,?m.extcredits2,?m.extcredits3,?m.extcredits4,?m.extcredits5,?
- ????m.extcredits6,?m.extcredits7,?m.extcredits8,?m.timeformat,?m.dateformat,?m.pmsound,?m.sigstatus,?m.invisible,?
- ????m.lastvisit,?m.lastactivity,?m.lastpost,?m.newpm,?m.accessmasks,?m.editormode,?m.customshow,?m.customaddfeed';?
- /*?
- ?*?將用戶表中所有需要讀取的字段放在一個字符串中,便于后面的sql語句調用?
- ?*/?
- ?
- if($sid)?{//如果變量$sid存在?
- ????if($discuz_uid)?{//如果cookie中有用戶uid,則根據uid在數據庫session表、member表中查詢取出相關字段值?
- ????????$query?=?$db->query("SELECT?s.sid,?s.styleid,?s.groupid='6'?AS?ipbanned,?s.pageviews?AS?spageviews,?s.lastolupdate,?s.seccode,?$membertablefields?
- ????????????FROM?{$tablepre}sessions?s,?{$tablepre}members?m?
- ????????????WHERE?m.uid=s.uid?AND?s.sid='$sid'?AND?CONCAT_WS('.',s.ip1,s.ip2,s.ip3,s.ip4)='$onlineip'?AND?m.uid='$discuz_uid'?
- ????????????AND?m.password='$discuz_pw'?AND?m.secques='$discuz_secques'");?
- ????}?else?{//如果cookie中沒有用戶uid,則僅僅從session表中取出相關信息?
- ????????$query?=?$db->query("SELECT?sid,?uid?AS?sessionuid,?groupid,?groupid='6'?AS?ipbanned,?pageviews?AS?spageviews,?styleid,?lastolupdate,?seccode?
- ????????????FROM?{$tablepre}sessions?WHERE?sid='$sid'?AND?CONCAT_WS('.',ip1,ip2,ip3,ip4)='$onlineip'");?
- ????}?
- ????/*?
- ?????*?groupid='6':IP?Banned?被禁止的ip?
- ?????*?MySQL的幾個實用字符串函數?:?
- ?????*?concat()可以連接一個或者多個字符串,但是在連接字符串的時候,只要其中一個是NULL,那么將返回NULL?
- ?????*?concat_ws()表示concat?with?separator,即有分隔符的字符串連接,在執行的時候,不會因為NULL值而返回NULL?
- ?????*?group_concat()可用來行轉列,?
- ?????*?repeat()用來復制字符串,如select?repeat('ab',2);'ab'表示要復制的字符串,2表示復制的份數?
- ?????*/?
- ?
- ?????>?
?
? ? ? 本文轉自許琴 51CTO博客,原文鏈接:http://blog.51cto.com/xuqin/882308,如需轉載請自行聯系原作者