搭建自己的PHP MVC框架詳解
本文詳細(xì)講述了搭建自己的PHP MVC框架的方法。分享給大家供大家參考,具體如下:
前言
說(shuō)到寫PHP的MVC框架,大家想到的第一個(gè)詞--“造輪子”,是的,一個(gè)還沒有深厚功力的程序員,寫出的PHP框架肯定不如那些出自大神們之手、經(jīng)過(guò)時(shí)間和各種項(xiàng)目考驗(yàn)的框架。但我還是準(zhǔn)備并且這么做了,主要是因?yàn)椋?/p>
認(rèn)為有關(guān)PHP的方方面面都了解了,但自己學(xué)習(xí)PHP的時(shí)間還短,基礎(chǔ)并不扎實(shí),很多常用函數(shù)的參數(shù)還偶爾要查手冊(cè),而且對(duì)于PHP的一些較新的特性如命名空間、反射等只是簡(jiǎn)單的看過(guò),并沒有能實(shí)際應(yīng)用過(guò)。
PHP的知識(shí)多且雜,一個(gè)普通的項(xiàng)目往住是業(yè)務(wù)邏輯代碼為主,而框架是一個(gè)能把這些知識(shí)點(diǎn)能融匯在一起的項(xiàng)目。
在自己寫一個(gè)框架的時(shí)候,也會(huì)參考一些我使用過(guò)的框架如TP/CI/YII等的源碼,在自己看源碼時(shí)也能幫助自己理解框架,更容易接受以后要使用的框架。
所以說(shuō),這次造輪子的目的不是為了造輪子而是為了在造輪子的過(guò)程中熟悉其工藝,總結(jié)輪子特點(diǎn),更好的使用輪子。
如果說(shuō)寫一個(gè)完整的PHP框架,那需要掌握的PHP知識(shí)點(diǎn)非常多,像設(shè)計(jì)模式、迭代器、事件與鉤子等等,還有許多基礎(chǔ)知識(shí)的靈活應(yīng)用。我自認(rèn)為這些還無(wú)法完全掌控,所以我的步驟是先自己搭建一個(gè)骨架,然后參考借鑒不同的PHP框架的特點(diǎn),將其慢慢完善。因?yàn)楣ぷ髟颍彝砩线€要補(bǔ)算法、網(wǎng)絡(luò)等編程基礎(chǔ),PHP框架部分可能只有周末有時(shí)間更新,我會(huì)在進(jìn)行框架功能更新之后,總結(jié)使用的知識(shí)點(diǎn),更新博文。
首先放上框架的目前源碼:GITHUB/zhenbianshu
或者點(diǎn)擊此處本站下載。
框架整體
首先自己總結(jié)一下PHP的MVC框架的工作流程:
簡(jiǎn)單來(lái)說(shuō),它以一個(gè)入口文件來(lái)接受請(qǐng)求,選擇路由,處理請(qǐng)求,返回結(jié)果。
當(dāng)然,幾句話總結(jié)完的東西實(shí)際上要做的工作很多,PHP框架會(huì)在每次接受請(qǐng)求時(shí),定義常量,加載配置文件、基礎(chǔ)類,根據(jù)訪問(wèn)的URL進(jìn)行邏輯判斷,選擇對(duì)應(yīng)的(模塊)控制器和方法,并且自動(dòng)加載對(duì)應(yīng)類,處理完請(qǐng)求后,框架會(huì)選擇并渲染對(duì)應(yīng)的模板文件,以html頁(yè)面的形式返回響應(yīng)。在處理邏輯的時(shí)候,還要考慮到錯(cuò)誤和異常的處理。
1、作為MVC框架,一定要有一個(gè)唯一的入口文件來(lái)統(tǒng)領(lǐng)全局,所有的訪問(wèn)請(qǐng)求都會(huì)首先進(jìn)入這個(gè)入口文件,如我框架根目錄的index.php,在里面,我定義了基本文件夾路徑,當(dāng)前環(huán)境,并根據(jù)當(dāng)前環(huán)境定義錯(cuò)誤報(bào)告的級(jí)別。
2、PHP中加載另外的文件,使用require和include,它們都是將目標(biāo)文件內(nèi)容加載到當(dāng)前文件內(nèi),替換掉require或include語(yǔ)句,require是加載進(jìn)來(lái)就執(zhí)行,而include是加載進(jìn)來(lái)在需要的時(shí)候執(zhí)行,而它們的_once結(jié)構(gòu)都是表示在寫多次的時(shí)候只執(zhí)行一次。
3、框架內(nèi)的配置變量等使用專用的配置文件來(lái)保存,這里我仿照了TP里的數(shù)組返回法,用了一個(gè)compileConf()
函數(shù)來(lái)解析數(shù)組,將數(shù)組的鍵定義為常量,值為數(shù)組的值。
if (!function_exists('compile_conf')) { function compileConf($conf) { foreach ($conf as $key => $val) { if(is_array($val)){ compileConf($val); }else{ define($key, $val); } } } } compileConf(require_once CONF_PATH.'config.php');
命名空間和自動(dòng)加載
為什么把命名空間和自動(dòng)加載放到一塊說(shuō)呢?
在一個(gè)PHP項(xiàng)目中,類特別多的時(shí)候,如果類名重復(fù)的話就會(huì)造成混亂,而且相同文件夾內(nèi)也不能存在同名的文件,所以這時(shí)候命名空間和文件夾就搭檔出場(chǎng)了。文件夾就是一個(gè)一個(gè)的盒子,命名空間在我理解就像是一個(gè)標(biāo)簽,盒子對(duì)應(yīng)標(biāo)簽。我們定義類時(shí),把各種類用不同的盒子分別裝好,并貼上對(duì)應(yīng)的標(biāo)簽。而在自動(dòng)加載類時(shí),我們根據(jù)標(biāo)簽(命名空間)可以很輕易找到對(duì)應(yīng)的盒子(文件夾)然后找到對(duì)應(yīng)的類文件。
而類的自動(dòng)加載,我們知道的__autoload()魔術(shù)函數(shù),它會(huì)在你實(shí)例化一個(gè)當(dāng)前路徑找不到的對(duì)象時(shí)自動(dòng)調(diào)用,根據(jù)傳入的類名,在函數(shù)體內(nèi)加載對(duì)應(yīng)的類文件。
現(xiàn)在我們多用spl_autoload_register()函數(shù),它可以注冊(cè)多個(gè)函數(shù)來(lái)代替__autoload函數(shù)的功能,我們傳入一個(gè)函數(shù)名為參數(shù),spl_autoload_register會(huì)將這個(gè)函數(shù)壓入棧中,在實(shí)例化一個(gè)當(dāng)前路徑內(nèi)找不到的類時(shí),系統(tǒng)將會(huì)將函數(shù)出棧依次調(diào)用,直到實(shí)例化成功。
spl_autoload_register('Sqier\Loader::autoLoad'); class Loader { public static function autoLoad($class) { //如果有的話,去除類最左側(cè)的\ $class = ltrim($class, '\\'); //獲取類的路徑全名 $class_path = str_replace('\\', '/', $class) . EXT; if (file_exists(SYS_PATH . $class_path)) { include SYS_PATH . $class_path; return; } if (file_exists(APP_PATH . $class_path)) { include APP_PATH . $class_path; return; } }
現(xiàn)在Loader類還是一個(gè)簡(jiǎn)單的類,待以后慢慢完善。
路由選擇
接下來(lái)就是路由選擇了,其本質(zhì)是根據(jù)當(dāng)前定義的全局URL模式選擇合適的方法來(lái)分析傳入的URI,加載對(duì)應(yīng)的類,并實(shí)現(xiàn)對(duì)應(yīng)的方法。
class Router { public static $uri; public static function bootstrap() { self::$uri = $_SERVER['REQUEST_URI']; switch (URL_MODE) { case 1: { self::rBoot(); break; } default: { self::rBoot(); } } } public static function rBoot() { $router = isset($_GET['r']) ? explode('/', $_GET['r']) : [ 'index', 'index' ]; $cName = 'Controller\\' . ucfirst($router[0]); $aName = isset($router[1]) ? strtolower($router[1]) . 'Action' : 'indexAction'; $controller = new $cName(); $controller->$aName(); } }
這樣,我在地址欄輸入 zbs.com/index.php?r=index/login 后,系統(tǒng)會(huì)自動(dòng)調(diào)用/app/Controller/Index.php下的login方法。完成了這么一個(gè)簡(jiǎn)單的路由。
階段總結(jié):
接下來(lái)我會(huì)優(yōu)化現(xiàn)有的工具類,添加顯示層,添加數(shù)據(jù)庫(kù)類,還會(huì)將一些別的框架里非常cool的功能移植進(jìn)來(lái)~
接上文(代碼有所更新),繼續(xù)完善框架(二):
對(duì)于本次更新,我想說(shuō):
① 本框架由本人挑時(shí)間完善,而我還不是PHP大神級(jí)的人物,所以框架漏洞難免,求大神們指出。
② 本框架的知識(shí)點(diǎn)應(yīng)用都會(huì)寫在博客里,大家有什么異議的可以一起討論,也希望看博客的也能學(xué)習(xí)到它們。
③ 本次更新,更新了函數(shù)規(guī)范上的一些問(wèn)題,如將函數(shù)盡量的獨(dú)立化,每一個(gè)函數(shù)盡量只單獨(dú)做好一件事情,盡量減少函數(shù)依賴。還對(duì)框架的整體優(yōu)化了一下,添加了SQ全局類,用以處理全局函數(shù),變量。
回調(diào)函數(shù)
替換了很low的類名拼裝實(shí)例化,然后拼裝方法名的用法,使用PHP的回調(diào)函數(shù)方式:
原代碼:
$controller_name = 'Controller\\' . self::$c_name; $action_name = self::$a_name . 'Action'; $controller = new $controller_name(); $controller->$action_name();
修改后代碼
$controller_name = 'Controller\\' . self::$c_name; $controller = new $controller_name(); call_user_func([ $controller, self::$a_name . 'Action' ]);
這里介紹一下PHP的函數(shù)回調(diào)應(yīng)用方式:call_user_func和call_user_func_array:
call_user_func ( callback $function [, mixed $parameter [, mixed $... ]] )
調(diào)用第一個(gè)參數(shù)所提供的用戶自定義的函數(shù)。
返回值:返回調(diào)用函數(shù)的結(jié)果,或FALSE。
call_user_func_array()
的用法跟call_user_func類似,只不過(guò)傳入的參數(shù)params整體為一個(gè)數(shù)組。
另外,call_user_func系列函數(shù)還可以傳入在第一個(gè)參數(shù)里傳入匿名參數(shù),可以很方便的回調(diào)某些事件,這些特性在復(fù)雜的框架里應(yīng)用也十分廣泛,如yii2的事件機(jī)制里回調(diào)函數(shù)的使用就是基于此。
VIEW層和ob函數(shù)
框架在controller的基類中定義了render方法來(lái)渲染頁(yè)面,它會(huì)調(diào)用類VIEW的靜態(tài)函數(shù)來(lái)分析加載對(duì)應(yīng)頁(yè)面的模板。
public static function display($data, $view_file) { if(is_array($data)) { extract($data);//extract函數(shù)解析$data數(shù)組中的變量 }else { //拋出變量類型異常 } ob_start(); ob_implicit_flush(0); include self::checkTemplate($view_file);//自定義checkTemplate函數(shù),分析檢查對(duì)應(yīng)的函數(shù)模板,正常返回路徑 $content = ob_get_clean(); echo $content; }
這里重點(diǎn)說(shuō)一下ob(output buffering)系列函數(shù),其作用引用簡(jiǎn)明代魔法的ob作用介紹:
① 防止在瀏覽器有輸出之后再使用setcookie,或者h(yuǎn)eader,session_start函數(shù)造成的錯(cuò)誤。其實(shí)這樣的用法少用為好,養(yǎng)成良好的代碼習(xí)慣。
② 捕捉對(duì)一些不可獲取的函數(shù)的輸出,比如phpinfo會(huì)輸出一大堆的HTML,但是我們無(wú)法用一個(gè)變量例如$info=phpinfo();來(lái)捕捉,這時(shí)候ob就管用了。
③ 對(duì)輸出的內(nèi)容進(jìn)行處理,例如進(jìn)行g(shù)zip壓縮,例如進(jìn)行簡(jiǎn)繁轉(zhuǎn)換,例如進(jìn)行一些字符串替換。
④ 生成靜態(tài)文件,其實(shí)就是捕捉整頁(yè)的輸出,然后存成文件,經(jīng)常在生成HTML,或者整頁(yè)緩存中使用。
它在ob_start()函數(shù)執(zhí)行后,打開緩沖區(qū),將后面的輸出內(nèi)容裝進(jìn)系統(tǒng)的緩沖區(qū),ob_implicit_flush(0)函數(shù)來(lái)關(guān)閉絕對(duì)刷送(echo等),最后使用ob_get_clean()函數(shù)將緩沖區(qū)的內(nèi)容取出來(lái)。
類__URL__常量和全局類
TP里的__URL__等全局常量用著很方便,可以很簡(jiǎn)單的實(shí)現(xiàn)跳轉(zhuǎn)等操作,而定義它的函數(shù)createUrl函數(shù)我又想重用,于是借鑒YII的全局類定義方法:
定義基類及詳細(xì)方法(以后的全局方法會(huì)寫在這里)
class BaseSqier{ //方法根據(jù)傳入的$info信息,和當(dāng)前URL_MODE解析返回URL字符串 public static function createUrl($info = '') { $url_info = explode('/', strtolower($info)); $controller = isset($url_info[1]) ? $url_info[0] : strtolower(CONTROLLER); $action = isset($url_info[1]) ? $url_info[1] : $url_info[0]; switch(URL_MODE){ case URL_COMMON: return "/index.php?r=" . $controller . '/' . $action; case URL_REWRITE: return '/' .$controller . '/' . $action; } } }
在啟動(dòng)文件中定義類并繼承基類;
require_once SQ_PATH.'BaseSqier.php'; class SQ extends BaseSqier{ }
在全局內(nèi)都可以直接使用SQ::createUrl()
方法來(lái)創(chuàng)建URL了。這樣,定義__URL__常量就很輕松了。
用單例模式定義數(shù)據(jù)庫(kù)連接基類
class Db { protected static $_instance; public static function getInstance() { if(!(self::$_instance instanceof self)) { self::$_instance = new self(); } return self::$_instance; } private function __construct() { $link = new \mysqli(DB_HOST, DB_USER, DB_PWD, DB_NAME) or die("連接數(shù)據(jù)庫(kù)失敗,請(qǐng)檢查數(shù)據(jù)庫(kù)配置信息!"); $link->query('set names utf8'); } public function __clone() { return self::getInstance(); } }
使用單例模式的核心是:
① 私有化構(gòu)造函數(shù),使無(wú)法用new來(lái)創(chuàng)建對(duì)象,也防止子類繼承它并改寫其構(gòu)造函數(shù);
② 用靜態(tài)變量存放當(dāng)前對(duì)象,定義靜態(tài)方法來(lái)返回對(duì)象,如對(duì)象還未實(shí)例化,實(shí)例化一個(gè),存入靜態(tài)變量并返回。
③ 構(gòu)造其__clone魔術(shù)方法,防止clone出一個(gè)新的對(duì)象;
DB類的sql查詢函數(shù)
DB查詢函數(shù)是一個(gè)很復(fù)雜的部分,它是一個(gè)自成體系的東西,像TP和YII的查詢方法都有其獨(dú)特的地方。我這里暫時(shí)先借用TP的MODEL基類,有時(shí)間再慢慢補(bǔ)這個(gè)。
嗯,介紹一下像TP的查詢里的方法聯(lián)查的實(shí)現(xiàn),其訣竅在于,在每個(gè)聯(lián)查方法的最后都用 return this 來(lái)返回已處理過(guò)的查詢對(duì)象。
階段總結(jié):
yii2里的數(shù)據(jù)表和model類屬性之間的映射很酷(雖然被深坑過(guò)), 前面一直避開的模塊(module,我可以想像得到把它也添加到URI時(shí)解析的麻煩)有時(shí)間考慮一下。
接上文,繼續(xù)完善框架(三)
本次更新的主要內(nèi)容有:
① 介紹了異常處理機(jī)制
② 完善了異常和錯(cuò)誤處理
③ 數(shù)據(jù)表跟Model類的映射
異常處理
異常處理:異常處理是編程語(yǔ)言或計(jì)算機(jī)硬件里的一種機(jī)制,用于處理軟件或信息系統(tǒng)中出現(xiàn)的異常狀況(即超出程序正常執(zhí)行流程的某些特殊條件)
異常處理用于處理程序中的異常狀況,雖說(shuō)是“異常狀態(tài)”,但仍然還是在程序編寫人員的預(yù)料之中,其實(shí)程序的異常處理完全可以用‘if else'語(yǔ)句來(lái)代替,但異常處理自然有其優(yōu)勢(shì)之處。
個(gè)人總結(jié)其優(yōu)點(diǎn)如下:
① 可以快速終止流程,重置系統(tǒng)狀態(tài),清理變量和內(nèi)存占用,在普通WEB應(yīng)用中,一次請(qǐng)求結(jié)束后,F(xiàn)AST CGI會(huì)自動(dòng)清理變量和上下文,但如果在PHP的命令行模式執(zhí)行守護(hù)腳本時(shí),它的效果就會(huì)很方便了。
② 大量的if else語(yǔ)句會(huì)使代碼變得繁雜難懂,使用異常處理可以使程序邏輯更清晰易懂,畢竟處理異常的入口只有catch語(yǔ)句一處。
③ 一量程序中的函數(shù)出現(xiàn)異常結(jié)果或狀況,如果使用函數(shù)的return方式返回異常信息,層層向上,每一次都要進(jìn)行return判斷。使用異常處理我們可以假設(shè)所有的返回信息都是正常的,避免了大量的代碼重復(fù)。
雖然將代碼放在try catch塊中會(huì)有微微的效率差,但是跟這些優(yōu)點(diǎn)一比,這點(diǎn)消耗就不算什么了。那么PHP的異常處理怎么使用呢?
PHP內(nèi)置有Exception類,使得我們可以通過(guò)實(shí)例化異常類來(lái)拋出異常。我們將代碼放在try語(yǔ)句中執(zhí)行,并在其后用catch試圖捕捉到在try代碼塊中拋出的異常,并對(duì)異常進(jìn)行處理。我們還可以在catch代碼段后使用finally語(yǔ)句塊,無(wú)論是否有異常都會(huì)執(zhí)行finally代碼塊的代碼,try catch語(yǔ)句形如下面代碼:
try{ throw new Exeption('msg'[,'code',$previous_exeception]); }catch(Exeption $var) { process($var); }catch(MyException $e){ process($e) }finally{ dosomething(); }
使用try catch語(yǔ)句,需要注意:
① 當(dāng)我們拋出異常時(shí),會(huì)實(shí)例化一個(gè)異常類,此異常類可以自己定義,但在catch語(yǔ)句中,我們需要規(guī)定要捕獲的異常對(duì)象的類名,并且只能捕獲到特定類的異常對(duì)象,當(dāng)然我們可以在最后捕獲一個(gè)異?;悾≒HP內(nèi)置異常類)來(lái)確保異常一定能被捕獲。
② 在拋出異常時(shí),程序會(huì)被終止,并回溯代碼找到第一個(gè)能捕獲到它的catch語(yǔ)句,try catch語(yǔ)句是可以嵌套的,并且如上面代碼所示 cacth語(yǔ)句是可以多次定義的。
③ finally塊會(huì)在try catch塊結(jié)束后執(zhí)行,即使在try catch塊中使用return返回,程序沒有執(zhí)行到最后。
框架里的異常處理
說(shuō)了那么多異常相關(guān)(當(dāng)然解釋這些也是為了能理解和使用框架),那么框架里要怎么實(shí)現(xiàn)呢?
重寫異常類
我們可以重寫異常類,完善其內(nèi)部方法:
<?php class Exception { protected $message = 'Unknown exception'; // 異常信息 protected $code = 0; // 異常代碼 protected $file; // 發(fā)生異常的文件名 protected $line; // 發(fā)生異常的代碼行號(hào) function __construct($message = null, $code = null,$previous_exeception = null); final function getMessage(); // 返回異常信息 final function getCode(); // 返回異常代碼 final function getFile(); // 返回發(fā)生異常的文件名 final function getLine(); // 返回發(fā)生異常的代碼行號(hào) final function getTrace(); // 返回異常trace數(shù)組 final function getTraceAsString(); // 返回異常trace信息 /** * 記錄錯(cuò)誤日志 */ protected function log(){ Logger::debug(); } }
如上,final方法是不可以重寫的,除此之外,我們可以定義自己的方法,如記錄異常日志,像我自定義的log方法,在catch代碼塊中,就可以直接使用$e->log來(lái)記錄一個(gè)異常日志了。
注冊(cè)全局異常方法
我們可以使用set_exception_handler('exceptionHandler')來(lái)全局捕獲沒有被catch塊捕獲到的異常,此異常處理函數(shù)需要傳入一個(gè)異常處理對(duì)象,這樣可以分析此異常處理信息,避免系統(tǒng)出現(xiàn)不人性化的提示,增強(qiáng)框架的健壯性。
function exceptionHandler($e) { echo '有未被捕獲的異常,在' . $e->getFile() . "的" . $e->getLine() . "行!"; }
其他全局函數(shù)
順便再說(shuō)一下其他的全局處理函數(shù):
① set_shutdown_function('shutDownHandler')
來(lái)執(zhí)行腳本結(jié)束時(shí)的函數(shù),此函數(shù)即使是在ERROR結(jié)束后,也會(huì)自動(dòng)調(diào)用。
② set_error_handler('errorHandler')
在PHP發(fā)生錯(cuò)誤時(shí)自動(dòng)調(diào)用,注意,必須在已注冊(cè)錯(cuò)誤函數(shù)后才發(fā)出的錯(cuò)誤才會(huì)調(diào)用。函數(shù)參數(shù)形式應(yīng)為($errno, $errstr, $errfile, $errline);
但是要注意這些全局函數(shù)需要在代碼段的前面已經(jīng)定義過(guò)再注冊(cè)。
數(shù)據(jù)表和Model類的ActiveRecord映射
初次使用yii2的ActivceRecord類覺得好方便,只需要定義其字段同名屬性再調(diào)用save方法就OK了(好神奇?。?,它是怎么實(shí)現(xiàn)的呢,看了下源碼,明白了其大致實(shí)現(xiàn)過(guò)程(基類)。
1. 使用‘describe table_name' 查詢語(yǔ)句;
2. 分析查詢結(jié)果:對(duì)每一個(gè)字段,有Field(字段名)、Type(數(shù)據(jù)類型)、Null(是否為空)、Key(索引信息,‘PRI'表示為主鍵)、Default(默認(rèn)值)、Extra(附加信息,如auto_increment)
3. 通過(guò)判斷其主鍵($row['KEY'] == 'PRI')信息,保存時(shí)看是否有主鍵信息,若存在,則為更新;不存在,則插入。
4. 另外,解析出來(lái)的字段信息還有更多妙用~~
更多關(guān)于php框架相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《php優(yōu)秀開發(fā)框架總結(jié)》、《codeigniter入門教程》、《ThinkPHP入門教程》、《Zend FrameWork框架入門教程》、《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》、《php+mysql數(shù)據(jù)庫(kù)操作入門教程》及《php常見數(shù)據(jù)庫(kù)操作技巧匯總》
希望本文所述對(duì)大家PHP程序設(shè)計(jì)有所幫助。
相關(guān)文章
php的instanceof和判斷閉包Closure操作示例
這篇文章主要介紹了php的instanceof和判斷閉包Closure操作,結(jié)合實(shí)例形式分析了PHP使用instanceof判斷類實(shí)例以及判斷閉包Closure相關(guān)操作技巧,需要的朋友可以參考下2020-01-01PHP實(shí)現(xiàn)批量生成App各種尺寸Logo
這篇文章主要介紹了PHP實(shí)現(xiàn)批量生成App各種尺寸Logo的方法和示例的核心代碼,非常的簡(jiǎn)單實(shí)用,這里推薦給小伙伴們,有需要的可以參考下。2015-03-03php根據(jù)某字段對(duì)多維數(shù)組進(jìn)行排序的方法
這篇文章主要介紹了php根據(jù)某字段對(duì)多維數(shù)組進(jìn)行排序的方法,實(shí)例分析了php操作數(shù)組及排序的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03Linux服務(wù)器下PHPMailer發(fā)送郵件失敗的問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于如何解決Linux服務(wù)器下PHPMailer發(fā)送郵件失敗的問(wèn)題,文中通過(guò)示例代碼介紹的很詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-03-03PHP實(shí)現(xiàn)關(guān)鍵字搜索后描紅功能示例
這篇文章主要介紹了PHP實(shí)現(xiàn)關(guān)鍵字搜索后描紅功能,結(jié)合實(shí)例形式分析了php數(shù)據(jù)庫(kù)連接、查詢、字符串轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2019-07-07微信公眾平臺(tái)開發(fā)教程①獲取用戶Openid及個(gè)人信息圖文詳解
這篇文章主要介紹了微信公眾平臺(tái)開發(fā)獲取用戶Openid及個(gè)人信息,結(jié)合圖文形式詳細(xì)分析了微信公眾平臺(tái)獲取用戶Openid及個(gè)人信息的步驟、操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2019-04-04