官术网_书友最值得收藏!

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_dumpvar_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&timestamp=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è)紅色圓

主站蜘蛛池模板: 大埔县| 香港 | 河间市| 荆门市| 高陵县| 温宿县| 东丰县| 十堰市| 邵武市| 寻乌县| 山东省| 南岸区| 焦作市| 谷城县| 万安县| 德庆县| 罗田县| 阿尔山市| 梨树县| 镇坪县| 曲阜市| 海南省| 师宗县| 江源县| 宁晋县| 高台县| 宁陵县| 噶尔县| 洪江市| 盐池县| 扬州市| 乐陵市| 通海县| 盘锦市| 沙洋县| 荥经县| 西昌市| 萨嘎县| 阳信县| 德钦县| 吕梁市|