- 微信公眾平臺(tái)應(yīng)用開發(fā)實(shí)戰(zhàn)(第2版)
- 鐘志勇 何威俊 馮煜博
- 7字
- 2019-01-01 00:44:55
1.5 相關(guān)技術(shù)介紹
1.5.1 PHP
PHP是一種創(chuàng)建動(dòng)態(tài)交互站點(diǎn)的強(qiáng)有力的服務(wù)器端腳本語言。PHP語法非常類似于Perl和C。PHP常常搭配Apache(web服務(wù)器)一起使用,不過它也支持ISAPI,并且可以運(yùn)行于Windows的微軟IIS平臺(tái)。
PHP和其他的語言相比,最大的優(yōu)勢是它集成了700多個(gè)內(nèi)建的函數(shù)且有非常多的擴(kuò)展,配合Apache服務(wù)器,使得它處理HTTP的請(qǐng)求非常方便。另一方面,它對(duì)MySQL的支持也非常好,有很多的訪問擴(kuò)展庫。本書會(huì)使用一種比較輕量的訪問MySQL的擴(kuò)展mysqli。接下來先介紹一下PHP的基本語法。由于本書的主題是微信公眾平臺(tái)的開發(fā),所以不會(huì)詳細(xì)介紹PHP的語法。如果你有C、C++或者Java等其他語言的開發(fā)經(jīng)驗(yàn),那么閱讀本書的PHP語法介紹之后就可以基本掌握PHP語法了。如果你沒有其他語言的開發(fā)經(jīng)驗(yàn),建議你購買一本專門介紹PHP語法的書籍學(xué)習(xí)。當(dāng)然如果你做過PHP的開發(fā),那可以直接跳過本節(jié)。
1.PHP的基本語法
PHP的腳本塊以“<?php”開始,以“?>”結(jié)束。可以把PHP的腳本塊放置在文檔中的任何位置。當(dāng)然,在支持簡寫的服務(wù)器上,可以使用“<?”和“?>”來開始和結(jié)束腳本塊。不過,為了達(dá)到最好的兼容性,推薦使用標(biāo)準(zhǔn)形式(<?php),而不是簡寫形式。下面的代碼就是PHP腳本的一個(gè)簡單應(yīng)用,其實(shí)現(xiàn)的是打印一個(gè)簡單的字符串:hello world!。
<?php echo "hello world!"; ?>
右擊Test工程,在彈出的快捷菜單中選擇“New”選項(xiàng),然后選擇“PHP File”選項(xiàng),在彈出的“新建PHP文件”對(duì)話框的“File Name”文本框中鍵入“test.php”,單擊“Finish”按鈕。接著在新建的test.php文件里輸入以上代碼,并通過Ctrl+S快捷鍵保存,如圖1-22所示。

圖1-22 test.php
然后打開瀏覽器,在地址欄里輸入http://127.0.0.1/Test/test.php,瀏覽器中將顯示“hello world!”,如圖1-23所示。

圖1-23 test.php在google Chrome中的運(yùn)行結(jié)果
test.php文件的創(chuàng)建和運(yùn)行展示了最基本的使用Apache和zendstudio開發(fā)和測試PHP腳本的過程:
1)在zendstudio中創(chuàng)建文件,并編寫相應(yīng)的代碼。
2)在瀏覽器中運(yùn)行腳本,查看腳本輸出是否正確。如果代碼有語法錯(cuò)誤或者出現(xiàn)了異常,瀏覽器會(huì)打印出語法錯(cuò)誤提示和拋出的異常描述。
本書中的所有PHP代碼都是這樣編寫并調(diào)試的。
PHP的注釋和C語言的注釋是一致的,用“//”作為單行注釋的開始,用“/*”和“*/”包含多行注釋,如圖1-24所示。

圖1-24 單行注釋和多行注釋
2.PHP的變量
PHP中所有的變量都是以$符號(hào)開始的。PHP是一門松散類型的語言,在變量設(shè)置中不需要明確聲明該變量的數(shù)據(jù)類型。根據(jù)變量被設(shè)置的方式,PHP會(huì)自動(dòng)把變量轉(zhuǎn)換為正確的數(shù)據(jù)類型。在C、C++和Java等強(qiáng)類型的語言中,必須在變量被使用前顯式聲明它的類型。另外,PHP的變量不需要提前聲明,在使用時(shí)會(huì)被自動(dòng)聲明。在Test中新建一個(gè)文件,命名為test2.php,鍵入以下代碼:
<?php $a = 5; echo $a; echo "<BR>"; $b = "this is a string"; echo $b; echo "<BR>"; echo "$a, $b"; echo "<BR>"; echo '$a, $b'; ?>
上述代碼的分析如下:
?echo "<BR>"是打印一個(gè)網(wǎng)頁換行符。
?我們聲明了兩個(gè)變量—$a和$b,因?yàn)榘?a設(shè)置成了5,所以PHP會(huì)自動(dòng)把$a設(shè)置成整型變量;同樣的,會(huì)把$b設(shè)置成字符串變量。
?注意其中的echo "$a, $b"和echo '$a, $b',對(duì)應(yīng)不同的輸出。在雙引號(hào)中變量會(huì)自動(dòng)解析成它的值,而在單引號(hào)中,變量不會(huì)做任何轉(zhuǎn)義。這和Shell腳本是一致的。
在瀏覽器中執(zhí)行http://127.0.0.1/Test/test2.php,結(jié)果如圖1-25所示。

圖1-25 test2.php的執(zhí)行結(jié)果
PHP中有一個(gè)非常簡便的連接操作符:.(一個(gè)英文的句號(hào))。它可以連接字符串,當(dāng)操作數(shù)中有非字符串時(shí),會(huì)先轉(zhuǎn)換成字符串,然后再做字符串連接操作。繼續(xù)上面的例子,我們?cè)谏厦娲a的末尾加入以下代碼:
<?php echo "<BR>"; echo $a . $b; echo "<BR>"; echo $a . $a; ?>
在瀏覽器中執(zhí)行修改后的代碼,結(jié)果如1-26所示。

圖1-26 連接操作符的結(jié)果
可以看到其他$a和$b的連接形成了“5this is a string”,$a先轉(zhuǎn)成了一個(gè)字符串再和$b做連接操作。在語句$a . $a中,連接操作符兩邊的$a都被轉(zhuǎn)成了字符串再做連接操作,于是得到結(jié)果“55”。
3.PHP的運(yùn)算符
PHP的運(yùn)算符包括算術(shù)運(yùn)算符、賦值運(yùn)算符、比較運(yùn)算符和邏輯運(yùn)算符。這些運(yùn)算符的定義和C、C++和Java中的都是一樣的,本書不做詳細(xì)介紹,下面以表格的方式列出這些運(yùn)算符的定義。
?算術(shù)運(yùn)算符,其具體定義如表1-1所示。
表1-1 算術(shù)運(yùn)算符
?賦值運(yùn)算符,其具體定義如表1-2所示。
表1-2 賦值運(yùn)算符
?比較運(yùn)算符,其具體定義如表1-3所示。
表1-3 比較運(yùn)算符
?邏輯運(yùn)算符,其具體定義如表1-4所示。
表1-4 邏輯運(yùn)算符
4.PHP數(shù)組
PHP的數(shù)組和其他語言的有很大的不同,這也是PHP語言的特色之一。PHP有三種類型的數(shù)組:數(shù)值數(shù)組、關(guān)聯(lián)數(shù)組和多維數(shù)組。
(1)數(shù)值數(shù)組
數(shù)值數(shù)組存儲(chǔ)的每一個(gè)元素都帶有一個(gè)數(shù)值ID鍵,這和C、C++、Java中的數(shù)組是一樣的。可以使用不同的方式來創(chuàng)建數(shù)值數(shù)組,例如下面這種定義方式:
<?php $animal = array("dog","cat","sheep"); ?>
這種以定義一個(gè)array的方式創(chuàng)建的數(shù)組,系統(tǒng)會(huì)自動(dòng)為數(shù)組中的每個(gè)值分配一個(gè)從0開始的鍵值。它等同于下面的方式:
<?php $animal[0] = "dog"; $animal[1] = "cat"; $animal[2] = "sheep"; ?>
下面我們測試一下采用上面這種方式新建的數(shù)組,建立新的測試文件或者刪除test2.php文件中的代碼,輸入下面的代碼:
<?php $animal[0] = "dog"; $animal[1] = "cat"; $animal[2] = "sheep"; echo $animal[0].",".$animal[1]."and".$anima[2]."are animals!" ?>
在瀏覽器中測試,結(jié)果如圖1-27所示。

圖1-27 數(shù)組測試結(jié)果
可以把數(shù)組的定義方式換成使用array的方式,得到的結(jié)果是一樣的。需要注意的一點(diǎn)是數(shù)組中各個(gè)元素的類型不需要一樣。可以定義以下的數(shù)組:
<?php $arr = array("string", 5, 1); ?>
這是PHP語言和其他語言(C/C++/Java)相比最大的不同之處。
(2)關(guān)聯(lián)數(shù)組
和數(shù)值數(shù)組不同,關(guān)聯(lián)數(shù)組的key可以為整型或者字符串。和數(shù)值數(shù)組一樣,關(guān)聯(lián)數(shù)組也有兩種定義方式。下面展示的是第一種方式:
<?php $animal = array( 'dog' => 23, 'cat' => 26, 'sheep' => 35 ); ?>
下面第二種方式:
<?php $animal['dog'] = 23; $animal['cat'] = 26; $animal['sheep'] = 35; ?>
這和C++的STL的map容器類似。但是它比map容器更靈活,因?yàn)镾TL的map在聲明的時(shí)候就規(guī)定好了key的類型和value的類型,PHP的關(guān)聯(lián)數(shù)組中每個(gè)key和每個(gè)value的類型都可以不一樣。以下的定義方式是合法的:
<?php $animal = array( 'dog' => 23, 'cat' => 26, 'sheep' => 35, 1 => 45, 0 => 47 ); ?>
這和下面的定義是一致的:
<?php $animal['dog'] = 23; $animal['cat'] = 26; $animal['sheep'] = 35; $animal[1] = 45; $animal[0] = 47; ?>
(3)多維數(shù)組
在多維數(shù)組中,如果主數(shù)組是一個(gè)數(shù)值數(shù)組,那么它的元素還可以是一個(gè)數(shù)組(關(guān)聯(lián)數(shù)組或者數(shù)值數(shù)組),如下面的代碼所示:
<?php $arr = array( array(1,2,3), array( "cat" => 10, "dog" => 21, "sheep" => 46 ) ); ?>
如果主數(shù)組是一個(gè)關(guān)聯(lián)數(shù)組,那么它的value可以是一個(gè)數(shù)組(關(guān)聯(lián)數(shù)組或者數(shù)值數(shù)組),如以下代碼所示:
<?php $arr = array( "cat" =>array(1,2,3), "dog" =>array( "cat" => 45, "dog" => 23 ) ); ?>
PHP的數(shù)組是PHP中最靈活的數(shù)據(jù)類型,可以任意組合各種數(shù)據(jù)類型,下面的數(shù)組也是合法的:
<?php $arr = array( 'hello', 'cat' => array('hello', 'world'), 'dog' => 1, 'sheep' ); ?>
PHP會(huì)為沒有key的元素自動(dòng)分配一個(gè)從0開始的key值,上面代碼中創(chuàng)建的數(shù)組中的元素'hello'的key為0,'sheep'的key為1。為了證實(shí)這個(gè)結(jié)論,我們先引入兩個(gè)PHP的內(nèi)建函數(shù)var_dump和var_export。
5.常用函數(shù)
(1)var_dump和var_export
var_dump可以把變量以字符串形式打印出來,而var_export可以把變量以字符串形式打印出來或者返回。先看下面的代碼:
<?php $arr = array( 'hello', 'cat' => array('hello', 'world'), 'dog' => 1, 'sheep' ); var_dump($arr); ?>
執(zhí)行結(jié)果如圖1-28所示。

圖1-28 var_dump測試結(jié)果
仔細(xì)查看圖1-28中的輸出,最開始的“array(4)”,表明var_dump函數(shù)的入?yún)ⅰ?srr”是一個(gè)大小為4的數(shù)組(數(shù)組第一維的長度為4)。接下來的一個(gè)花括號(hào)包含的是這個(gè)數(shù)組的說明,數(shù)組的每一個(gè)元素的key都在一個(gè)中括號(hào)中,然后每個(gè)元素的value是由類型和它的值組成的。仔細(xì)觀察數(shù)組中每個(gè)元素的下標(biāo):第一個(gè)元素“hello”的下標(biāo)為0,最后一個(gè)元素“sheep”的下標(biāo)為1;其他的元素的下標(biāo)都是我們?cè)诔绦蛑兄付ǖ摹_@證實(shí)了我們的結(jié)論:對(duì)沒有設(shè)定下標(biāo)的元素,PHP會(huì)自動(dòng)為它們?cè)O(shè)定從0開始的下標(biāo)。
var_export函數(shù)和var_dump類似,不同之處是它接受第二個(gè)參數(shù):一個(gè)bool值(true/false)。當(dāng)var_export不傳入第二個(gè)參數(shù)的時(shí)候,它輸出變量的字符串表示的是和var_dump的類似(此時(shí)第二個(gè)參數(shù)的為默認(rèn)值false)的。當(dāng)設(shè)定第二個(gè)參數(shù)為true時(shí),它會(huì)把變量的字符串當(dāng)作返回值返回,而不是直接輸出。看下面的例子:
<?php $arr = array( 'hello', 'cat' => array('hello', 'world'), 'dog' => 1, 'sheep' ); $ret = var_export($arr, true); echo "var_export result: " . $ret ; ?>
執(zhí)行結(jié)果如圖1-29所示。

圖1-29 var_export的輸出
仔細(xì)觀察var_export的輸出會(huì)發(fā)現(xiàn),和var_dump的輸出不同之處是它不輸出類型信息。但是這些類型信息基本上可以一眼看出。
var_dump和var_export這兩個(gè)函數(shù)給調(diào)試代碼帶來了非常大的方便。有了這兩個(gè)函數(shù)可以在需要查看復(fù)雜變量值的時(shí)候,加入類似上面例子中的代碼。可能學(xué)過Java、使用過Eclipse的人更傾向于使用zendstudio內(nèi)建的調(diào)試功能,因?yàn)閦endstuio也是基于Eclipse開發(fā)的。我們不評(píng)論這兩種方法的優(yōu)劣,只談偏好吧。筆者覺得通過寫日志的方式來查看程序邏輯可能存在的錯(cuò)誤是最合適的,因?yàn)樗梢哉鎸?shí)地反映代碼執(zhí)行的先后順序。另外PHP是一種腳本語言,不需要編譯即可執(zhí)行,這樣在修改完代碼后到執(zhí)行之間不需要花很多時(shí)間來編譯。根據(jù)筆者多年的PHP開發(fā)經(jīng)驗(yàn),使用這種寫日志的方式來排錯(cuò)是非常高效的。既然談到寫日志,下面介紹一個(gè)最簡單的寫日志的函數(shù)file_put_contents。
(2)file_put_contents
file_put_contents是一個(gè)可以寫字符串到文件的函數(shù),有四個(gè)入?yún)ⅲ覀冎灰P(guān)心前三個(gè)。第一個(gè)入?yún)⑹潜硎疚募刂返淖址坏诙€(gè)是需要寫入的字符串;第三個(gè)是寫入的模式,我們一般用宏定義FILE_APPEND,表示追加寫入。看下面的例子。
<?php file_put_contents("C:\\AppServ\\tmp.txt", "start\n", FILE_APPEND); $arr = array( 'hello', 'cat' => array('hello', 'world'), 'dog' => 1, 'sheep' ); $ret = var_export($arr, true); file_put_contents("C:\\AppServ\\tmp.txt","var_export result:".$ret."\n",FILE_APPEND); file_put_contents("C:\\AppServ\\tmp.txt", "end\n", FILE_APPEND); ?>
執(zhí)行這段代碼,然后打開文件tmp.txt,查看file_put_contents輸出,如圖1-30所示。

圖1-30 file_put_contents輸出
我們?cè)趫?zhí)行這段之前并沒有手工建立tmp.txt文件,這說明file_put_contents在沒有文件的情況下會(huì)自動(dòng)創(chuàng)建文件。另外var_export的輸出和在Chrome里輸出的不一樣,文件里的格式更好看,因?yàn)閷?duì)齊了。這是因?yàn)関ar_export是用ASCII碼中的\t和來做對(duì)齊處理的,但是這些字符串的意義在瀏覽器中是不能正常解析的,但是在文本處理器editplus中可以正常解析。
PHP的基本語法就介紹到這里,它的其他的特性和其他語言相差不大,在后面介紹其他預(yù)備知識(shí)和具體使用到的時(shí)候再介紹。
介紹到這里,我有一些經(jīng)驗(yàn)和讀者分享。學(xué)習(xí)一門語言不能上來就使用一本很厚的書,這樣花很長時(shí)間看完書,接受了太多的信息,反而不知道從何處下手。從最基本最簡單的開始,邊用邊學(xué)新的特性,這樣會(huì)學(xué)得更扎實(shí)、更實(shí)用。在使用一段時(shí)間后再通讀一下大部頭的名著,才會(huì)真正吸收里面的內(nèi)容,使自己對(duì)這門語言有全面的了解,讓自己有實(shí)質(zhì)性的提高。
1.5.2 HTTP
HTTP(HyperText Transport Protocol)即超文本傳輸協(xié)議,它詳細(xì)規(guī)定了瀏覽器和萬維網(wǎng)服務(wù)器之間互相通信的規(guī)則,是通過因特網(wǎng)傳送萬維網(wǎng)文檔的數(shù)據(jù)傳送協(xié)議。Rfc2612(http://www.ietf.org/rfc/rfc2616.txt)使用了176頁的“天書”來描述這個(gè)協(xié)議。微信后臺(tái)在向我們的公眾平臺(tái)服務(wù)器發(fā)消息的時(shí)候(圖1-1中的第2步)使用的就是HTTP協(xié)議。看起來我們好像遇到了大麻煩,幸運(yùn)的是我們并不需要看完這些文檔才能開始使用它。在使用PHP來做HTTP的服務(wù)端的時(shí)候,Apache為我們解析好了大部分的協(xié)議,并提供了非常方便的方法以供我們獲取需要的信息。其中,我們需要關(guān)心的是兩個(gè)全局變量$_GET和$HTTP_RAW_ POST_DATA。Apache解析好請(qǐng)求中的HTTP協(xié)議,把請(qǐng)求中g(shù)et數(shù)據(jù)和post變量寫入$_GET和$HTTP_RAW_POST_DATA變量中。我們只要直接使用$_GET和$HTTP_RAW_POST_DATA就能取到微信后臺(tái)傳給我們的數(shù)據(jù)。
筆者在meiri10futu的部署機(jī)器上運(yùn)行tcpdump抓包,圖1-31是抓到并用wireshark分析的一個(gè)完整的請(qǐng)求和返回包。

圖1-31 公眾平臺(tái)服務(wù)器的HTTP請(qǐng)求和返回包
圖1-31所示的數(shù)據(jù)中,“HTTP/1.1 200 OK”之前的部分是微信后臺(tái)發(fā)給公眾賬號(hào)服務(wù)器的請(qǐng)求(圖1-1中的第2步),之后的部分是公眾賬號(hào)服務(wù)器返回給微信后臺(tái)的數(shù)據(jù)。我們現(xiàn)在重點(diǎn)關(guān)注請(qǐng)求部分,先把這部分?jǐn)?shù)據(jù)以文本形式粘貼到下面:
POST /interface.php?signature=771f6bd01508e46b02061b0a1330a6ad67e1ad77&time stamp=1364458805&nonce=1364226029 HTTP/1.0 User-Agent: Mozilla/4.0 Accept: */* Host: 42.96.142.129 Content-Type: text/xml Content-Length: 276 Pragma: no-cache Connection: Keep-Alive <xml><ToUserName><![CDATA[gh_a8b0ebbe91f5]]></ToUserName> <FromUserName><![CDATA[owI97jpNpdfSpOsR9kG97g_wRtvY]]></FromUserName> <CreateTime>1364458805</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[...]]></Content> <MsgId>5860305944314258860</MsgId> </xml>
請(qǐng)求分為頭部(head)和正文(body)兩部分,數(shù)據(jù)之間用一個(gè)空行分割(實(shí)際上是兩個(gè)“\r\n”),即上面的“Keep-Alive”下面的空行之后的數(shù)據(jù)為body,之前的部分為head。接下來我們來看GET數(shù)據(jù)和POST數(shù)據(jù)在哪里。
(1)HTTP請(qǐng)求中的GET數(shù)據(jù)
注意上面的請(qǐng)求數(shù)據(jù)中的第一行中如下的數(shù)據(jù):
signature=771f6bd01508e46b02061b0a1330a6ad67e1ad77×tamp=136445880 5&nonce=1364226029
這些用“&”分隔的字符串就是GET數(shù)據(jù),我們把用“&”分割之后的字符串列出來:
?signature=771f6bd01508e46b02061b0a1330a6ad67e1ad77 ?timestamp=1364458805 ?nonce=1364226029
這樣看起來就非常清楚了,等號(hào)左邊是GET數(shù)據(jù)的key,右邊是GET數(shù)據(jù)的value,具體如何在PHP程序中獲取這些GET數(shù)據(jù),等講完P(guān)OST數(shù)據(jù)之后會(huì)有一個(gè)統(tǒng)一的例子。
(2)HTTP請(qǐng)求中POST數(shù)據(jù)
上面請(qǐng)求中的body部分就是POST數(shù)據(jù),POST數(shù)據(jù)和GET數(shù)據(jù)不同,它不會(huì)有key值。POST數(shù)據(jù)一般都比較大,GET數(shù)據(jù)往往比較小。下面的程序展示了如何獲取GET數(shù)據(jù)和POST數(shù)據(jù)。我們把test2.php的內(nèi)容改成如下形式:
<?php file_put_contents("C:\\AppServ\\tmp.txt", var_export($_GET, true), FILE_APPEND); file_put_contents("C:\\AppServ\\tmp.txt", "\n", FILE_APPEND); file_put_contents("C:\\AppServ\\tmp.txt", var_export($HTTP_RAW_POST_DATA, true), FILE_APPEND); ?>
然后我們使用Fiddler的Request Bilder向test2.php發(fā)一個(gè)POST請(qǐng)求,請(qǐng)求中帶的GET數(shù)據(jù)及POST數(shù)據(jù)和上面的示例一樣,如圖1-32所示。

圖1-32 用Fiddler向test2.php發(fā)POST請(qǐng)求
然后查看tmp.txt中的輸出,如圖1-33所示。

圖1-33 GET和POST數(shù)據(jù)在文本中的輸出
注意 在Fiddler中輸入GET數(shù)據(jù)時(shí)不是在Request Header輸入框中輸入,而是在請(qǐng)求URL后先輸入一個(gè)問號(hào),然后輸入所有的GET字符串。
可以看到,$_GET是一個(gè)數(shù)組;key是各個(gè)GET數(shù)據(jù)的key;$HTTP_RAW_ POST_DATA是一個(gè)字符串,包含body的所有內(nèi)容。在設(shè)置這些變量的同時(shí),Apache還為我們?cè)O(shè)置了一個(gè)$_POST變量,它是用來存前臺(tái)HTML的form表達(dá)提交的數(shù)據(jù),本書使用不到。大家不要誤以為$_POST變量是我們想要的POST數(shù)據(jù),一定要區(qū)分開。
注意 我們?cè)谶@里沒有使用editplus來打開tmp.txt,而是用的寫字板。如果你試了用editplus打開的話會(huì)發(fā)現(xiàn),腳本中我們特意輸出和var_export自動(dòng)輸出的“\n”都沒有當(dāng)成換行符來顯示,使得格式很難看。這是因?yàn)楫?dāng)文件中既有“\r\n”又有“\n”時(shí),editplus會(huì)自動(dòng)選擇其中的一種作為換行符,而忽略其他的方式。在打開tmp.txt時(shí)editplus就選擇了HTTP的body中的“\r”作為換行符而忽略了“\n”。
在這一小節(jié)中我們用到了三個(gè)非常有用的工具:
1)tcpdump是一個(gè)Linux上的抓包命令,有非常多的選項(xiàng)和表達(dá)式。可以結(jié)合自己的需求,使用相應(yīng)的選項(xiàng)和表達(dá)式來抓取需要的網(wǎng)絡(luò)包。
2)wireshark是一個(gè)Windows上的抓包工具。它的功能和tcpdump是一樣的,但是它的優(yōu)點(diǎn)在于其圖形化界面,對(duì)數(shù)據(jù)包的可視化做得非常好,同時(shí)也能打開tcpdump的數(shù)據(jù)包文件。我就喜歡用tcpdump在Linux機(jī)器上抓包之后再到Windows上用wireshark打開來分析。
3)fiddler是一個(gè)非常強(qiáng)大的HTTP調(diào)試代理,它能夠記錄并檢查所有你的電腦和互聯(lián)網(wǎng)之間的HTTP通信,設(shè)置斷點(diǎn),查看fiddle中所有“進(jìn)出”的數(shù)據(jù)(指cookie、HTML,JS,CSS等文件,這些都可以讓你胡亂修改)。我們這里使用它來代替Chrome瀏覽器構(gòu)造請(qǐng)求包,因?yàn)镃hrome瀏覽器不能讓我們靈活地自定義GET數(shù)據(jù)和POST數(shù)據(jù)。請(qǐng)讀者到fiddler的官網(wǎng)(http://www.fiddler2.com/ fiddler2/)下載fiddler并安裝好,稍后還會(huì)用到它。
使用好這些工具對(duì)于自己的工作和學(xué)習(xí)有非常大的幫助。有興趣的讀者可以到網(wǎng)絡(luò)上搜索這些工具的高級(jí)教程。
HTTP協(xié)議相關(guān)的內(nèi)容非常多,本書中需要用到的就只有這些了。讀者如果有興趣的話可以專門找一些介紹HTTP協(xié)議的資料進(jìn)行學(xué)習(xí),或者認(rèn)真閱讀一下rfc2612。
1.5.3 XML
XML(Extensible Markup Language)是一種可擴(kuò)展的標(biāo)記語言,可以用來標(biāo)記數(shù)據(jù)、定義數(shù)據(jù)類型,是一種允許用戶對(duì)自己的標(biāo)記語言進(jìn)行定義的源語言。關(guān)于它的完整的、標(biāo)準(zhǔn)的描述,可以參看w3c的官方文檔http://www.w3.org/ TR/REC-xml/。之所以這里要講這種語言,是因?yàn)槲⑿藕笈_(tái)發(fā)給我們的公眾賬號(hào)服務(wù)器的POST數(shù)據(jù)就是用這種語言描述的。上一小節(jié)中的$HTTP_RAW_POST_ DATA就是XML描述的數(shù)據(jù)。似乎我們又遇到了大麻煩,不過很幸運(yùn)的是,我們不需要關(guān)注XML語言的太多細(xì)節(jié),只要知道如何用PHP解析它就可以了。
XML的語法的基本模式是:分別用“<itemName>”和“</itemName>”來表示一個(gè)條目的開始和結(jié)束,“itemName”可以是任意字符。每個(gè)條目也可以有屬性,在開始的<itemName attrName1=“1”attrName2=“2” >中表示,微信后臺(tái)給我們發(fā)的XML沒有屬性字段。條目的值可以是數(shù)字和字符串,也可以是一個(gè)或者多個(gè)條目,也就是可以嵌套。字符串用“<![CDATA[text]]>”表示,其中text可以是任意字符串。用CDATA包起來的目的是讓text中的內(nèi)容不會(huì)解析成別的條目,只會(huì)解析成字符串。整個(gè)XML文檔必須要有一個(gè)根條目,該條目可以是任意名字。微信后臺(tái)的信息中的根條目是以“<xml>”開始,以“</xml>”結(jié)束的。仔細(xì)觀察圖1-33所示的POST數(shù)據(jù),整個(gè)XML文檔有6個(gè)條目,都沒有嵌套。第一個(gè)條目是ToUserName,它是一個(gè)字符串,值為“gh_a8b0ebbe91f5”;第三個(gè)條目是CreateTime,它是一個(gè)整型變量,值是1364458805。其他四個(gè)條目也都類似。
PHP為我們提供了一個(gè)很簡單的函數(shù)來解析XML—simplexml_load_string。它接受五個(gè)參數(shù),第一個(gè)是需要解析的XML字符串,其他四個(gè)參數(shù)都是可選的。在解析成功的時(shí)候會(huì)返回一個(gè)SimpleXMLElement的對(duì)象,如果解析失敗則返回false。一般在入?yún)⒅袀魅氲腦ML數(shù)據(jù)不符合規(guī)范的時(shí)候才會(huì)解析失敗進(jìn)而返回false。代碼清單1-1所示的例子展示了如何使用simplexml_load_string來解析XML。這個(gè)例子繼續(xù)上一節(jié),我們用simplexml_load_string來解析$HTTP_RAW_ POST_DATA,并輸出解析后的數(shù)據(jù)。
代碼清單1-1
<?php $postXmlStr = $HTTP_RAW_POST_DATA; $xmlObj = simplexml_load_string($postXmlStr); if(false === $xmlObj) { echo "parse xml string error! \n"; exit(0); } $toUser = $xmlObj->ToUserName; echo "to User: " . $toUser . "\n"; $fromUser = $xmlObj->FromUserName; echo "from User: " . $fromUser . "\n"; $createTime = $xmlObj->CreateTime; echo "create time: " . $createTime . "\n"; $msgType = $xmlObj->MsgType; echo "msg type: " . $msgType . "\n"; $content = $xmlObj->Content; echo "content: " . $content . "\n"; $msgId = $xmlObj->MsgId; echo "msg id: " . $msgId . "\n"; ?>
重新按照上一節(jié)的方法,用fiddler向test2.php發(fā)送請(qǐng)求,然后單擊選中fiddler左邊的請(qǐng)求列表中對(duì)應(yīng)到這次請(qǐng)求的條目,雙擊,在右側(cè)會(huì)顯示請(qǐng)求的詳細(xì)返回信息,如圖1-34所示。

圖1-34 fiddler中的請(qǐng)求test2.php的結(jié)果
可以在圖1-34所示界面右下側(cè)的TextView中看到我們?cè)诔绦蛑衑cho的輸出。可以看到我們是通過對(duì)象$xmlObj的成員變量來訪問XML的條目的,引用成員的方式和C++中通過對(duì)象指針訪問它的public成員變量是一樣的。PHP的對(duì)象在本書后面的實(shí)例中會(huì)用到。限于篇幅,這里不過多介紹,讀者可以查閱其他資料做更多的了解。
注意 在代碼清單1-1中判斷simplexml_load_string的結(jié)果是否為false使用的是三個(gè)等于號(hào)“===”。這是PHP的“全等于”符號(hào),只有當(dāng)全等號(hào)左邊和右邊的表達(dá)式的大小和類型完全相等時(shí)才返回true。雖然PHP是弱類型語言,但并不表示變量沒有類型。兩個(gè)等于號(hào)(“==”)只能判斷兩邊的值是否相等。在兩個(gè)等于號(hào)的情況下:false、空字符串、空數(shù)組、0都是相等的。但是使用全等號(hào)的判斷可以把它們區(qū)分開,因?yàn)樗鼈兊念愋筒幌嗤?/p>
1.5.4 MySQL
MySQL是開源的關(guān)系型數(shù)據(jù)庫,我們介紹它是因?yàn)樵诤竺娴陌咐校枰鎯?chǔ)用戶信息的時(shí)候會(huì)用到它。相信很多人在學(xué)習(xí)數(shù)據(jù)庫的時(shí)候都學(xué)過SQL Server,MySQL和它類似,都支持?jǐn)?shù)據(jù)存儲(chǔ)和SQL語句查詢。和SQL Server相比,MySQL顯得更輕便簡潔,并且移植性更好。對(duì)于MySQL來說,Windows的版本和Linux的版本使用方法完全一樣。
Java和.NET等語言在訪問數(shù)據(jù)庫的時(shí)候通常都是通過一些封裝好的db connector或者driver實(shí)現(xiàn)的,PHP也一樣。PHP訪問MySQL的封裝庫有三種連接方式:ext/mysqli、PDO_MySql和ext/mysql。ext/mysqli是ext/mysql的增強(qiáng)版(i代表improved),并且ext/mysql已經(jīng)不再做升級(jí)了,因此不推薦使用ext/mysql。表1-5詳細(xì)比較了這三種連接MySQL封裝庫方式的異同。
表1-5 MySQL的三種PHP連接方式的異同
本書中使用ext/mysqli來訪問MySQL,下面就描述一下如何使用ext/mysqli。ext/mysqli的每個(gè)函數(shù)都包含一個(gè)面向?qū)ο箫L(fēng)格的函數(shù)和一個(gè)過程化風(fēng)格函數(shù)。這以過程化風(fēng)格的函數(shù)為例進(jìn)行講解。連接并發(fā)起對(duì)MySQL數(shù)據(jù)庫的查詢,一般有以下幾個(gè)步驟:
1)使用mysqli_init初始化ext/mysqli,這個(gè)函數(shù)會(huì)返回一個(gè)ext/mysqli資源,用來做下一個(gè)函數(shù)的入?yún)ⅰ?/p>
2)使用mysqli_real_connect打開一個(gè)對(duì)MySQL服務(wù)器的連接,這個(gè)函數(shù)需要MySQL服務(wù)器的地址、端口、數(shù)據(jù)庫名、用戶名和密碼。
3)使用mysqli_query進(jìn)行查詢SQL語句的操作。
4)如果上一步中的返回結(jié)果是一個(gè)數(shù)據(jù)集(select操作的返回),可使用mysqli_fetch_array獲取返回結(jié)果集。這個(gè)函數(shù)有兩個(gè)參數(shù):第一個(gè)參數(shù)用于表示mysqli_query的返回?cái)?shù)據(jù)集;第二個(gè)參數(shù)用于描述mysqli_fetch_array的返回是關(guān)聯(lián)數(shù)組(MYSQLI_ASSOC)、數(shù)值數(shù)組(MYSQLI_NUM)或者兩種都返回(MYSQLI_BOTH),兩種都返回是默認(rèn)方式,不過我們這里使用第一種方式,只返回關(guān)聯(lián)數(shù)組。
5)使用mysqli_close關(guān)閉和數(shù)據(jù)庫的連接,這個(gè)步驟可以省略,因?yàn)镻HP在腳本執(zhí)行完畢的時(shí)候會(huì)釋放并關(guān)閉所有的fd,其中也包括對(duì)數(shù)據(jù)庫的連接。
我們首先創(chuàng)建用來測試的數(shù)據(jù)庫和數(shù)據(jù)表。登錄phpMyAdmin,然后創(chuàng)建一個(gè)名為test2的數(shù)據(jù)庫(MySQL默認(rèn)會(huì)生成一個(gè)名為test的數(shù)據(jù)庫),如圖1-35所示。

圖1-35 使用phpMyAdmin創(chuàng)建test2數(shù)據(jù)庫
數(shù)據(jù)庫創(chuàng)建完成后會(huì)進(jìn)入數(shù)據(jù)庫test2的管理界面。在管理界面中選擇名為SQL的tab,在輸入框中輸入建表語句,如圖1-36所示。

圖1-36 使用phpMyadmin創(chuàng)建表test
可以看到使用phpMyAdmin管理MySQL數(shù)據(jù)庫非常方便,現(xiàn)在已經(jīng)有了數(shù)據(jù)庫和數(shù)據(jù)表,我們?cè)趜endstudio中把test.php的代碼替換成以下形式:
<?php $conn = mysqli_init(); if(!$conn) { echo "mysqli_init error !"; exit(0); } $ret = mysqli_real_connect($conn, "localhost", "root", "root", "test2", 3306, "test", MYSQLI_CLIENT_FOUND_ROWS); if(!$ret) { echo "mysqli_real_connect error!"; exit(0); } //插入記錄 $sql = 'insert into test values(1, "tom" )'; $ret = mysqli_query($conn, $sql); if(!$ret) { echo "mysqli_query error!"; exit(0); } //查詢記錄 $sql = 'select * from test'; $ret = mysqli_query($conn, $sql); //獲取查詢結(jié)果 while (($row = mysqli_fetch_array($ret, MYSQLI_ASSOC)) != NULL) { echo "id: " . $row['id'] . "<BR>"; echo "name: " . $row['name'] . "<BR>"; } ?>
在Chrome中執(zhí)行上面的程序,結(jié)果如圖1-37所示。

圖1-37 數(shù)據(jù)庫操作示例的執(zhí)行結(jié)果
我們?cè)诔绦虻淖詈蟛]有調(diào)用mysqli_close,這也是大部分的腳本的做法。當(dāng)然如果你堅(jiān)持認(rèn)為這樣不好,并一定要加上它,那也是沒有問題的。
1.5.5 HTML5
HTML5在微信公眾平臺(tái)推出以后又火了一把。“微信的入口夢應(yīng)由HTML5承載”,“微信創(chuàng)業(yè)猜想:社交游戲?qū)⒁⑿臜TML5游戲”,在百度上用關(guān)鍵字“微信”和“HTML5”搜索,立即可以看到很多類似標(biāo)題的文章。2010年2月,喬布斯極力推崇用HTML5代替flash,并且指出了flash的很多問題和HTML5的很多優(yōu)點(diǎn),這讓HTML5瞬間到了風(fēng)口浪尖。后來Adobe公司自己也放棄繼續(xù)開發(fā)flash的移動(dòng)版本,這更加讓人堅(jiān)信HTML5將會(huì)大面積普及。隨后發(fā)生的一件事讓大家對(duì)HTML5又重新冷靜地思考了一把:Facebook的HTML5的移動(dòng)版本因?yàn)樾阅軉栴}被原生版本代替。現(xiàn)在微信公眾平臺(tái)的誕生又讓大家把目光聚集到了HTML5上。新技術(shù)出現(xiàn)的時(shí)候,IT圈的新聞總是讓人很糾結(jié)。到底HTML5前途怎么樣,估計(jì)沒有人能夠看得清。在這種情況下,腳踏實(shí)地地做一些事情會(huì)是好的選擇。如果大家想嘗試在微信上開發(fā)HTML5的應(yīng)用,可以看一下接下來對(duì)HTML5的介紹。
HTML5是用于取代1999年所制定的HTML 4.01和XHTML 1.0 標(biāo)準(zhǔn)的HTML標(biāo)準(zhǔn)版本,現(xiàn)在仍處于發(fā)展階段,但大部分瀏覽器已經(jīng)支持某些 HTML5技術(shù)。簡單地說,HTML5其實(shí)是HTML4的一個(gè)更高級(jí)的版本,并不是一個(gè)完全的新技術(shù)。往往人們提到HTML5時(shí)還包括CSS的新版本CSS3,以及相關(guān)的JavaScript。通過這一整套的技術(shù),在瀏覽器上可以實(shí)現(xiàn)復(fù)雜的富界面應(yīng)用。HTML5在頁面上表現(xiàn)能力一點(diǎn)兒也不遜于PC原生的應(yīng)用。而在HTML4上實(shí)現(xiàn)這些應(yīng)用往往要一些其他的插件的支持,如Adobe Flash、Microsoft Silverlight、Oracle JavaFX等。
目前大部分的瀏覽器都支持HTML5,包括國外的Firefox(火狐瀏覽器)、IE9及其更高版本、Chrome(谷歌瀏覽器)、Safari、Opera等,及國內(nèi)的傲游瀏覽器(Maxthon)、基于IE或Chromium(Chrome的工程版或稱實(shí)驗(yàn)版)所推出的360瀏覽器、搜狗瀏覽器、QQ瀏覽器、獵豹瀏覽器等。需要注意的是,這些瀏覽器稍早的版本都不支持HTML5,特別是IE,而國內(nèi)的大部分用戶都是使用IE的低版本來瀏覽網(wǎng)頁的,在實(shí)際設(shè)計(jì)產(chǎn)品的時(shí)候需要特別注意這一點(diǎn)。
微信是一個(gè)手機(jī)上的應(yīng)用,沒有PC版本。因此我們更關(guān)心的是移動(dòng)端是否支持HTML5。好消息是無論是IOS還是Android都對(duì)HTML5有很好的支持。壞消息是HTML5目前還存在性能問題,由于手機(jī)處理能力和PC相比還有差距,所以大規(guī)模的HTML5應(yīng)用在手機(jī)端運(yùn)行還是會(huì)有問題的。不過可以肯定的是,手機(jī)的處理能力在不斷地、快速地提升,如今四核的手機(jī)已經(jīng)很常見了,所以也許在不久的將來,大規(guī)模的HTML5應(yīng)用在手機(jī)端的運(yùn)行將不再有問題。
相比HTML4,HTML5有以下的一些新的特性:
?語義特性,HTML5賦予網(wǎng)頁更好的意義和結(jié)構(gòu)。隨著HTML5對(duì)RDFa的微數(shù)據(jù)與微格式等方面的支持,其所支持的標(biāo)簽將更加豐富,其將可構(gòu)建對(duì)程序、對(duì)用戶都更有價(jià)值的數(shù)據(jù)驅(qū)動(dòng)的Web。HTML5提供了更多的標(biāo)簽,表1-6是這些新增標(biāo)簽的簡介。
表1-6 HTML5新增標(biāo)簽
?本地存儲(chǔ)特性。基于HTML5開發(fā)的網(wǎng)頁APP擁有更短的啟動(dòng)時(shí)間、更快的聯(lián)網(wǎng)速度,這些全得益于HTML5 APP Cache,以及本地存儲(chǔ)功能。
?設(shè)備兼容特性。自從Geolocation功能的API文檔公開以來,HTML5為網(wǎng)頁應(yīng)用開發(fā)者們提供了更多功能上的優(yōu)化選擇,帶來了更多體驗(yàn)功能的優(yōu)勢。HTML5提供了前所未有的數(shù)據(jù)與應(yīng)用接入開放接口,使外部應(yīng)用可以直接與瀏覽器內(nèi)部的數(shù)據(jù)直接相連,例如視頻影音可直接與microphones及攝像頭相連。
?連接特性。更有效的連接工作效率,使得基于頁面的實(shí)時(shí)聊天、更快速的網(wǎng)頁游戲體驗(yàn)、更完美的在線交流得到了實(shí)現(xiàn)。HTML5擁有更有效的服務(wù)器推送技術(shù),Server-Sent Event和WebSockets就是其中的兩個(gè)特性,這兩個(gè)特性能夠幫助我們實(shí)現(xiàn)服務(wù)器將數(shù)據(jù)“推送”到客戶端的功能。服務(wù)端推送技術(shù)一直是Web端服務(wù)的難題。現(xiàn)有的技術(shù)有基于長連接的COMET、內(nèi)嵌flash、基于ajax的長輪詢,這些都有各自的缺點(diǎn),不是HTML原生支持的。
?網(wǎng)頁多媒體特性。支持網(wǎng)頁端的Audio、Video等多媒體功能,與網(wǎng)站自帶的APPS、攝像頭、影音等功能相得益彰。
?三維圖形及特效特性。基于SVG、Canvas、WebGL及CSS3的3D功能,用戶會(huì)驚嘆于在瀏覽器中所呈現(xiàn)的視覺效果。
?性能與集成特性。沒有用戶會(huì)永遠(yuǎn)等待你的Loading—HTML5會(huì)通過 XMLHttpRequest2等技術(shù),幫助您的Web應(yīng)用和網(wǎng)站在多樣化的環(huán)境中更快速地工作。
?CSS3特性。在不犧牲性能和語義結(jié)構(gòu)的前提下,CSS3中提供了更多的風(fēng)格和更強(qiáng)的效果。此外,較之以前的Web版本,HTML5的開放字體格式(WOFF)也提供了更高的靈活性和控制性。
在這些特性中,最受關(guān)注的是其中的三維圖形的特性,也就是HTML5提供的<canvas>標(biāo)簽。HTML的其他標(biāo)簽就像一個(gè)一個(gè)的圖形,我們可以自定義圖標(biāo)的顏色、大小以及單擊它之后的響應(yīng),然后把它“貼”到網(wǎng)頁上。而這個(gè)<canvas>標(biāo)簽就像是給了我們一個(gè)畫板,在網(wǎng)頁上給了我們一塊區(qū)域,可以讓我們?cè)谏厦嬷苯赢嫵鋈魏挝覀冃枰男螤睿⑶铱梢皂憫?yīng)我們的輸入。下面給出一個(gè)非常簡單的示例,這個(gè)示例實(shí)現(xiàn)的是使用canvas在網(wǎng)頁上畫一個(gè)紅色的圓形。新建一個(gè)PHP文件,輸入以下的代碼:
<html> <body> <canvas id="myCanvas" width="200" height="100" style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <script type="text/javascript"> var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); cxt.fillStyle="#FF0000"; cxt.beginPath(); cxt.arc(70,18,15,0,Math.PI*2,true); cxt.closePath(); cxt.fill(); </script> </body> </html>
在Chrome中執(zhí)行上述PHP文件,結(jié)果如圖1-38所示。
仔細(xì)觀察上面JavaScript代碼中對(duì)變量cxt的使用,會(huì)發(fā)現(xiàn)這些函數(shù)調(diào)用方式和openGL的一些基本函數(shù)很像。Canvas給了瀏覽器畫圖的能力,提供了圖形學(xué)的一些基本函數(shù),基于這些基本函數(shù),開發(fā)者可以開發(fā)出復(fù)雜的二維、三維的應(yīng)用。
HTML5的內(nèi)容非常多,這里沒有辦法面面俱到,我們只能給出一個(gè)簡單的介紹,有興趣的讀者可以找相關(guān)的資料深入學(xué)習(xí)。

圖1-38 使用canvas畫一個(gè)紅色圓
- 中臺(tái)架構(gòu)與實(shí)現(xiàn):基于DDD和微服務(wù)
- Android開發(fā)進(jìn)階:從小工到專家
- IT服務(wù)供應(yīng)鏈協(xié)調(diào)
- 數(shù)字化運(yùn)維:IT運(yùn)維架構(gòu)的數(shù)字化轉(zhuǎn)型
- 企業(yè)IT架構(gòu)轉(zhuǎn)型之道:阿里巴巴中臺(tái)戰(zhàn)略思想與架構(gòu)實(shí)戰(zhàn)
- 微服務(wù)治理:體系、架構(gòu)及實(shí)踐
- 數(shù)據(jù)科學(xué)家訪談錄
- 微信公眾平臺(tái)搭建與開發(fā)揭秘(第2版)
- SRv6網(wǎng)絡(luò)編程:開啟IP網(wǎng)絡(luò)新時(shí)代
- IT項(xiàng)目管理理論與方法
- Axure RP8 網(wǎng)站和APP原型制作 從入門到精通
- 這才是用戶體驗(yàn)設(shè)計(jì):人人都能看懂的產(chǎn)品設(shè)計(jì)書
- IT能力與企業(yè)信息化
- IT服務(wù)管理及CMMI-SVC實(shí)施
- IT與項(xiàng)目管理軟件應(yīng)用