PHP進(jìn)階
發(fā)表時(shí)間:2023-08-07 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]PHP功能的強(qiáng)大首先在于它的大量的內(nèi)置函數(shù)庫(kù),它可以讓初學(xué)者也能執(zhí)行許多復(fù)雜的任務(wù),而不必安裝新的庫(kù)和關(guān)心底層的詳細(xì)情況,而這恰恰是其它流行的諸如Perl這樣的客戶端語言所不具備的。由于這個(gè)教程的限...
PHP功能的強(qiáng)大首先在于它的大量的內(nèi)置函數(shù)庫(kù),它可以讓初學(xué)者也能執(zhí)行許多復(fù)雜的任務(wù),而不必安裝新的庫(kù)和關(guān)心底層的詳細(xì)情況,而這恰恰是其它流行的諸如Perl這樣的客戶端語言所不具備的。由于這個(gè)教程的限制,我們僅僅學(xué)習(xí)了一些與MySQL數(shù)據(jù)庫(kù)相關(guān)的一些函數(shù)(事實(shí)上,即使是這種函數(shù),我們也沒有學(xué)全)。在這最后的部分,我們會(huì)稍微擴(kuò)大一下范圍來看看其它對(duì)于建立一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的網(wǎng)站有用的PHP的特征。
我們首先將學(xué)習(xí)PHP的include函數(shù),使用這個(gè)函數(shù),我們可以在許多頁(yè)面中重復(fù)使用同一個(gè)PHP代碼段。我們還看到如何利用這個(gè)函數(shù)提高我們的網(wǎng)站的安全性。
雖然PHP一般是相當(dāng)快速和有效率的,但是它會(huì)加重服務(wù)器的運(yùn)行時(shí)間和工作量。對(duì)于一個(gè)高流量的站點(diǎn)來說,這種負(fù)擔(dān)可能會(huì)達(dá)到無法接受的程度。但是這并不意味著我們需要放棄我們的站點(diǎn)的數(shù)據(jù)庫(kù)驅(qū)動(dòng)的特征。我們會(huì)看到如何使用PHP在后臺(tái)建立半動(dòng)態(tài)頁(yè)面而不必過分加重服務(wù)器的負(fù)擔(dān)。
經(jīng)常有人問論壇是如何利用一個(gè)<INPUT TYPE=FILE>標(biāo)記來接受文件的上載的。我們也將學(xué)習(xí)到如何用PHP實(shí)現(xiàn)這種功能,而且我們還會(huì)看到如果將其有效地結(jié)合到一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的站點(diǎn)中。
最后,PHP還有一個(gè)相當(dāng)強(qiáng)大的功能是可以很簡(jiǎn)單地將動(dòng)態(tài)生成的內(nèi)容很方便地作為email的信息發(fā)送出去。不論你是想要使用PHP使得訪問者將你的站點(diǎn)的內(nèi)容發(fā)送給它們的朋友,還是僅僅想提供一種方法讓用戶可以得到他們遺忘的口令,PHP的email函數(shù)都可以很好地實(shí)現(xiàn)這種功能!
PHP的服務(wù)器端包含
如果你已經(jīng)在Internet上工作過一段時(shí)間,你也許接觸過服務(wù)器端包含(SSI)這個(gè)術(shù)語;如果你沒有接觸過,你可以參看有關(guān)這個(gè)問題的Matt Mickiewicz的迷你指南。
從本質(zhì)上說,SSI允許你將存儲(chǔ)在你的Web服務(wù)器上的某一個(gè)文件的內(nèi)容插入到另一個(gè)文件中去,最常見的應(yīng)用是將一個(gè)網(wǎng)站的公用設(shè)計(jì)部分寫入一個(gè)小的HTML文件當(dāng)中,這個(gè)文件可以被Web頁(yè)面所包含。對(duì)這個(gè)小文件的所有變動(dòng)立即會(huì)影響所有包含它的文件。就象一個(gè)PHP腳本一樣,Web的瀏覽者不需要對(duì)此有所了解,因?yàn)閃eb服務(wù)器會(huì)在將被請(qǐng)求的頁(yè)面發(fā)送到瀏覽器之前做好所有的工作。
PHP有一個(gè)函數(shù)可以提供類似的功能。但是在包含文件中除了可以含有正式的HTML以及其它靜態(tài)的內(nèi)容以外,還可以含有腳本程序。讓我們來看看下面這個(gè)例子:
<!-- include-me.inc --><br>
<?php<br>
echo( "<P>Soylent Green is made from people! ");<br>
?><br>
在上面的文件中,include-me.inc包含了一些簡(jiǎn)單的PHP代碼。請(qǐng)注意這個(gè)文件的文件名的結(jié)尾是.inc,而不是.php。這表示這個(gè)文件與一般的Web服務(wù)器所認(rèn)為的PHP腳本有所不同。這會(huì)保證這個(gè)文件只有被插入到一個(gè).php文件中才會(huì)被執(zhí)行,此外這也有助于你分辨你的PHP Web頁(yè)面和PHP包含文件。
你還需要下面的文件:
<!-- testinclude.php -->
<HTML>
<HEAD>
<TITLE> Test of PHP Includes </TITLE>
</HEAD>
<BODY>
<?php
include("include-me.inc");
?>
</BODY>
</HTML>
這個(gè)文件和我們以前使用過的PHP腳本差不多,它的擴(kuò)展名是.php extension(如果你的服務(wù)器需要,也可以是.php3)。請(qǐng)注意對(duì)include函數(shù)的調(diào)用。我們指定了我們要插入的文件名(include-me.inc),PHP會(huì)試圖獲取這個(gè)文件并將其插入到現(xiàn)在的文件中以取代include。將這兩個(gè)文件都上載到你的Web服務(wù)器(或者將它們拷貝到你的Web服務(wù)器的文檔文件夾,如果你正在這個(gè)服務(wù)器上工作的話),然后用你的瀏覽器裝載testinclude.php。你會(huì)看到一個(gè)包含我們的插入文件信息的Web頁(yè)面的,一切和你當(dāng)初預(yù)料的沒有什么兩樣。
如果這個(gè)例子不能工作,你也許需要配置你的php.ini文件中的include_path選項(xiàng)。用你常用的文本編輯器打開這個(gè)文件找到以include_path開始的一行(一般是在文件的中間)。就象你所熟悉的系統(tǒng)PATH環(huán)境變量的設(shè)置一樣,這里包含了一個(gè)目錄的列表,PHP會(huì)從這些位置尋找你所要包含的文件。它應(yīng)該包括"."(當(dāng)前目錄)。
根據(jù)你的服務(wù)器的操作系統(tǒng)是Windows還是UNIX,你也許需要確定是否要用到引號(hào):
對(duì)于UNIX:
include_path=.:/another/directory
對(duì)于Windows:
include_path=".;c:anotherdirectory"
利用插入文件提高安全性
PHP腳本有時(shí)會(huì)包含一些諸如用戶名、口令以及其它一些你不想公開的敏感信息。你也許已經(jīng)使用過mysql_connect函數(shù),這個(gè)函數(shù)要求你在需要訪問數(shù)據(jù)庫(kù)的PHP腳本中輸入你的MySQL用戶名和口令。雖然你可以簡(jiǎn)單地對(duì)MySQL進(jìn)行設(shè)置以使得這個(gè)用戶名和口令只能供PHP使用,而為會(huì)被可能出現(xiàn)的黑客利用(通過在第八章中學(xué)習(xí)的方法對(duì)用戶表的主機(jī)字段進(jìn)行設(shè)置),你也許還是需要其它的比較方便的對(duì)你的用戶名和口令的保護(hù)。
“但是等一下,”可能你會(huì)這樣說,“因?yàn)镻HP是由服務(wù)器處理的,沒人會(huì)看到我的口令,對(duì)嗎?”不錯(cuò),但是你是否考慮到這樣一個(gè)情況,服務(wù)器對(duì)PHP的解析可能會(huì)因?yàn)榕既坏脑蚨V埂_@可能是因?yàn)槟硞(gè)善意的同事對(duì)軟件的錯(cuò)誤配置,也可能是因?yàn)槠渌囊蛩兀绻l(fā)生了這樣的情況,PHP頁(yè)面會(huì)當(dāng)成純文本文件來處理,于是你的所有的PHP代碼(包括你的口令)將是完全公開的!
為了預(yù)防這樣的安全漏洞,你可以將包含敏感信息的代碼放到一個(gè)插入文件中,然后將它放到一個(gè)不屬于你的Web服務(wù)的目錄結(jié)構(gòu)的目錄中去。將這個(gè)目錄添加到你的PHP的include_path中(在php.ini中添加),你可以指定PHP包含函數(shù)所使用的目錄,而不必?fù)?dān)心其中文件的安全,因?yàn)槟愕腤eb服務(wù)器不會(huì)將其作為Web頁(yè)來顯示。
例如,如果你的Web服務(wù)器定義所有的Web頁(yè)面必須存在于/home/httpd/及其子目錄中,你可以建立一個(gè)名為/home/phplib/來保存你的包含文件。將這個(gè)目錄添加到你的include_path中,這樣就行了!下面的例子顯示了如何將你的數(shù)據(jù)庫(kù)連接代碼放置到一個(gè)包含文件中:
<!-- dbConnect.inc (in /home/phplib/) -->
<?php
$cnx = mysql_connect("localhost",
"root", "rootpassword");
?>
以及一個(gè)使用這個(gè)包含文件的文件:
<!-- dbSample.php (in /home/httpd/) -->
<?php
// Connect to MySQL
include("dbConnect.inc");
mysql_select_db("myDatabase",$cnx);
...
正如你看到的,如果你的服務(wù)器的PHP停止了工作,被暴露的僅僅是對(duì)包含函數(shù)的調(diào)用。用戶名和口令被安全地存儲(chǔ)在dbConnect.inc中,而這個(gè)文件不能從網(wǎng)站直接訪問。
半動(dòng)態(tài)頁(yè)面
作為一個(gè)成功的(或者是即將成功的)網(wǎng)站的所有者,你肯定希望你的網(wǎng)站的訪問量越來越高。不幸的是,大的訪問量恰恰也是Web服務(wù)器的管理者所擔(dān)心的事--特別是當(dāng)網(wǎng)站主要是由動(dòng)態(tài)生成的、由數(shù)據(jù)庫(kù)驅(qū)動(dòng)的頁(yè)面組成的時(shí)候,情況更是這樣。這樣的頁(yè)面與處理傳統(tǒng)的HTML文件相比,意味著運(yùn)行Web服務(wù)軟件的計(jì)算機(jī)的巨大負(fù)擔(dān)。因?yàn)閷?duì)每一個(gè)頁(yè)面的請(qǐng)求都相當(dāng)于在計(jì)算機(jī)上運(yùn)行一個(gè)小程序。
雖然數(shù)據(jù)庫(kù)驅(qū)動(dòng)的站點(diǎn)的有些頁(yè)面必須嚴(yán)格地實(shí)時(shí)地從數(shù)據(jù)庫(kù)中調(diào)用相關(guān)的數(shù)據(jù),但是大部分頁(yè)面的要求并不這么嚴(yán)格。例如對(duì)于一個(gè)Web站點(diǎn)的首頁(yè)來說,典型的情況是,它會(huì)向訪問者簡(jiǎn)要介紹這個(gè)站點(diǎn)最近做了哪些更新。但是事實(shí)上這樣的更新多長(zhǎng)時(shí)間做一次呢?一天一次?還是一周一次?而且對(duì)于你的站點(diǎn)的訪問者來說非常及時(shí)地了解這些更新又有多大價(jià)值?也許對(duì)于這類變動(dòng)在你網(wǎng)站上的反應(yīng)稍微有點(diǎn)滯后也不會(huì)有多大問題。
通過將高訪問量的動(dòng)態(tài)頁(yè)面轉(zhuǎn)換成“半動(dòng)態(tài)”的頁(yè)面,也就是一個(gè)可以按照一定時(shí)間間隔“刷新”內(nèi)容以動(dòng)態(tài)地重新生成的靜態(tài)頁(yè)面,你可以大大減輕你的Web服務(wù)器處理數(shù)據(jù)庫(kù)驅(qū)動(dòng)的頁(yè)面的工作量。
例如,我們有一個(gè)名為index.php的首頁(yè),它提供你的網(wǎng)站的新內(nèi)容的摘要。通過對(duì)服務(wù)器日志的檢查,你可以發(fā)現(xiàn)這是你的網(wǎng)站中被訪問得最多的頁(yè)面之一。通過前面的討論,你應(yīng)該已經(jīng)意識(shí)到對(duì)于這個(gè)頁(yè)面我們并不需要在每次請(qǐng)求時(shí)動(dòng)態(tài)地生成。只在在每次將新內(nèi)容添加到你的站點(diǎn)的同時(shí)更新這個(gè)頁(yè)面,就可以充分保證它的動(dòng)態(tài)效果了。使用一個(gè)PHP程序,你可以生成動(dòng)態(tài)輸出的頁(yè)面一個(gè)靜態(tài)的“映像”,你可以將其命名為index.html以取代原來的動(dòng)態(tài)頁(yè)面。
我們需要學(xué)習(xí)一些有關(guān)文件讀取、寫入以及修改的知識(shí)。PHP可以很好地完成這些工作,只是我們之前沒看到過這些我們所需要的函數(shù):
fopen
打開一個(gè)文件,以用來進(jìn)行讀寫操作。這個(gè)文件可以存儲(chǔ)在服務(wù)器的硬盤上,也可以通過一個(gè)URL裝載。
fclose
通知PHP你將不再對(duì)某一文件進(jìn)行讀寫操作,釋放它以備其它程序或腳本使用。
fread
將一個(gè)文件的數(shù)據(jù)讀入到一個(gè)PHP變量中。允許你指定讀取多少信息(也就是多少字符或多少字節(jié))。
fwrite
將一個(gè)來自于PHP變量的數(shù)據(jù)寫入到文件中。
copy
執(zhí)行一個(gè)文件拷貝的操作。
unlink
從硬盤中刪除一個(gè)文件。
你明白了嗎?如果還沒有,不要擔(dān)心--等一下我們會(huì)進(jìn)行詳細(xì)講解。
建立一個(gè)名為generateindex.php的文件。它用來模擬一個(gè)Web瀏覽器從index.php(你的首頁(yè)的動(dòng)態(tài)版本)讀取內(nèi)容,并將其以靜態(tài)文本的格式寫入index.html中。如果在這個(gè)處理過程中發(fā)生了什么錯(cuò)誤,我們需要避免對(duì)原來的index.html“好的”拷貝的破壞,所以我們的這個(gè)腳本先將新的靜態(tài)版本寫入到一個(gè)臨時(shí)文件(tempindex.html)中,如果中途沒發(fā)生什么問題,再用其覆蓋index.html。
下面是generateindex.php的代碼,我們加入足夠多的注解以保證你能順利地讀懂這段程序:
<!-- generateindex.php -->
<?php
//設(shè)置我們將要使用的文件
$srcurl = "http://localhost/index.php";
$tempfilename = "tempindex.html";
$targetfilename = "index.html";
?>
<HTML>
<HEAD>
<TITLE>
Generating <?php echo("$targetfilename"); ?>
</TITLE>
</HEAD>
<BODY>
<P>Generating <?php echo("$targetfilename"); ?>...</P>
<?php
//首先刪除上次操作可能遺留下來的臨時(shí)文件。
//這個(gè)過程可能會(huì)提示錯(cuò)誤,所以我們使用@以防止報(bào)錯(cuò)。
@unlink($tempfilename);
//通過一個(gè)URL的請(qǐng)求裝入動(dòng)態(tài)版本。
//在我們接收到相關(guān)內(nèi)容之前,Web服務(wù)器會(huì)對(duì)PHP進(jìn)行處理
//(因?yàn)楸举|(zhì)上我們是在模擬一個(gè)Web瀏覽器),
//所以我們將獲得的是一個(gè)靜態(tài)的HTML頁(yè)面。
//'r'指出我們只要求對(duì)這個(gè)“文件”進(jìn)行讀操作。
$dynpage = fopen($srcurl, 'r');
//處理錯(cuò)誤
if (!$dynpage) {
echo("<P>Unable to load $srcurl. Static page ".
"update aborted!</P>");
exit();
}
//將這個(gè)URL的內(nèi)容讀入到一個(gè)PHP變量中。
//指定我們將讀取1MB的數(shù)據(jù)(超過這個(gè)數(shù)據(jù)量一般是意味著出錯(cuò)了)。
$htmldata = fread($dynpage, 1024*1024);
//當(dāng)我們完成工作后,關(guān)閉到源“文件”的連接。
fclose($dynpage);
//打開臨時(shí)文件(同時(shí)在這個(gè)過程中建立)以用來寫入(注意'w'的用法).
$tempfile = fopen($tempfilename, 'w');
//處理錯(cuò)誤
if (!$tempfile) {
echo("<P>Unable to open temporary file ".
"($tempfilename) for writing. Static page ".
"update aborted!</P>");
exit();
}
//將靜態(tài)頁(yè)面的數(shù)據(jù)寫入到臨時(shí)文件中
fwrite($tempfile, $htmldata);
//完成寫入后,關(guān)閉臨時(shí)文件。
fclose($tempfile);
//如果到了這里,我們應(yīng)該已經(jīng)成功地寫好了一個(gè)臨時(shí)文件,
//現(xiàn)在我們可以用它來覆蓋原來的靜態(tài)頁(yè)面了。
$ok = copy($tempfilename, $targetfilename);
//最后刪除這個(gè)臨時(shí)文件。
unlink($tempfilename);
?>
<P>Static page successfully updated!</P>
</BODY>
</HTML>
上面的代碼看上去很令人恐怖,其實(shí)這只是因?yàn)槲覀冊(cè)谄渲邪舜罅康淖⑨尅h除這些注釋。你會(huì)看到這段代碼其實(shí)很簡(jiǎn)單。
現(xiàn)在,每當(dāng)我們運(yùn)行g(shù)enerateindex.php(也就是說通過一個(gè)瀏覽器請(qǐng)求這個(gè)頁(yè)面),就會(huì)從index.php生成一個(gè)新刷新的index.html。通過將index.php和generateindex.php移動(dòng)到一個(gè)訪問有限制的目錄,你可以保證只有站點(diǎn)管理者能夠更新你的主頁(yè)。將這個(gè)腳本作一下擴(kuò)充,你可以生成你的站點(diǎn)上的所有的半動(dòng)態(tài)頁(yè)面,你還可以在你的內(nèi)容管理系統(tǒng)中增加“更新主頁(yè)”的連接!
如果你希望你的主頁(yè)能夠自動(dòng)地刷新,你只需要設(shè)置你的服務(wù)器定期地運(yùn)行g(shù)enerateindex.php(例如說,每隔一個(gè)小時(shí))。在新的Windows 9x下,你可以使用任務(wù)管理程序(對(duì)于舊的版本,你必須使用補(bǔ)丁包)每隔一個(gè)小時(shí)自動(dòng)運(yùn)行php.exe。你只需要建立一個(gè)包含以下行的名為generateindex.bat的批處理文件。
C:PHPphp.exe C:WWWgenerateindex.php
如果必要的話,對(duì)路徑和文件名進(jìn)行調(diào)整,然后設(shè)置任務(wù)管理程序每隔一個(gè)小時(shí)執(zhí)行一次generateindex.bat(你需要設(shè)置24個(gè)每天在固定時(shí)間運(yùn)行的任務(wù))。
在Linux下(或其它基于UNIX的平臺(tái)下)你可以使用cron--一個(gè)可以在各種UNIX系統(tǒng)下用來定義任務(wù)和運(yùn)行時(shí)間的程序來完成類似的工作。如果你對(duì)cron還不了解的話,你可以請(qǐng)教你熟悉的Linux專家,或者查閱你熟悉的Linux網(wǎng)站。
你使用cron設(shè)置任務(wù)和我們之前討論的在Windows下的用法很類似。但是,你需要一個(gè)單機(jī)版的PHP,這不是指在第一章中編譯的Apache中可導(dǎo)入的PHP模塊。你需要對(duì)用來編譯Apache模塊的同一軟件包中單獨(dú)地進(jìn)行編譯。如果你需要幫助,你可以參看軟件包提供的說明,也可以查閱PHP官方網(wǎng)站!
對(duì)于一個(gè)有經(jīng)驗(yàn)的cron使用者來說,你只需要在你的crontab文件中增加下面的這一行:
0 0-23 * * * php /path/to/generateindex.php > /dev/null
處理文件的上載
到目前為止,這個(gè)教程中數(shù)據(jù)庫(kù)驅(qū)動(dòng)的站點(diǎn)的所有例子都是處理的文本的數(shù)據(jù)。笑話、文章、作者...所有的這些都可以完全由文本形式的字符串來表示。但是如果你在運(yùn)行時(shí),有一個(gè)在線的數(shù)字圖庫(kù)需要人們能夠上載用數(shù)字照相機(jī)拍攝的圖片,我們就需要允許訪問者向我們的站點(diǎn)上載他們的圖片,我們也需要相應(yīng)的處理程序。
讓我們先從基本的開始:寫一個(gè)HTML表單用來供用戶上載文件。在HTML中,這很簡(jiǎn)單,只需要用一個(gè)<INPUT TYPE=FILE>標(biāo)志就行了。但是,默認(rèn)情況是只有用戶選擇的文件名被發(fā)送了。要通過表單數(shù)據(jù)提交文件自身,我們需要在<FORM>標(biāo)志中增加ENCTYPE="multipart/form-data":
<FORM ACTION="fileupload.php" METHOD=POST
ENCTYPE="multipart/form-data">
<P>Select file to upload:
<INPUT TYPE=FILE NAME="uploadedfile"></P>
<P><INPUT TYPE=SUBMIT NAME="submit" VALUE="Submit"></P>
</FORM>
正如我們看到的,一個(gè)PHP腳本(fileupload.php)會(huì)處理前面的表單提交的數(shù)據(jù)。也正如你所預(yù)計(jì)的,一個(gè)名為$uploadedfile(由<INPUT>標(biāo)志的NAME屬性確定)的PHP變量會(huì)自動(dòng)被建立。但是,$uploadedfile中存儲(chǔ)的不是上載文件的內(nèi)容,而是存儲(chǔ)在Web服務(wù)器上的硬盤中的文件的文件名,這個(gè)文件被保存在TEMP環(huán)境變量所指定的目錄中(例如,對(duì)于絕大多數(shù)的Windows 9x系統(tǒng),這個(gè)目錄將是C:WindowsTEMP)。這個(gè)文件僅僅在用來處理該表單提交的PHP腳本運(yùn)行時(shí)存在,所以如果你想有什么其它用途(例如,將它存起來以供網(wǎng)站顯示),你需要使用我們?cè)谇懊嫣岬降腸opy函數(shù)在其它地方做一份拷貝。
伴隨著$uploadedfile,三個(gè)其它的變量也會(huì)同時(shí)被建立。$uploadedfile_name包含了文件提交前的文件名(已提交的文件將在TEMP目錄中以phpx的文件名存儲(chǔ),這里x是一個(gè)數(shù)字),$uploadedfile_size說明了文件的大小(以字節(jié)表示),而$uploadedfile_type說明了MIME類型(例如text/plain、image/gif等等)。記住,"uploadedfile"僅僅是提交文件的INPUT標(biāo)志中的NAME,實(shí)際的文件名將存在上述變量中。
你可以根據(jù)這些變量以決定是否接受一個(gè)上載的文件。例如,在我們的圖庫(kù)中我們僅僅只對(duì)JPEG或GIF文件感興趣。這些文件的MIME類型應(yīng)該是image/pjpeg或image/gif,所以確認(rèn)上載文件的代碼大致上是這樣大:
if ("image/pjpeg" == $uploadedfile_type
or "image/gif" == $uploadedfile_type) {
// Handle the file...
} else {
echo("<P>Please submit a JPEG or GIF image file. ");
}
雖然你可以使用一個(gè)類似的技術(shù)以拒絕過大的文件(通過檢查$uploadedfile_size變量),但是通常這不是一個(gè)好主意。在得到這個(gè)變量之前,這個(gè)文件已經(jīng)被上載并保存在TEMP目錄中。如果你試圖因?yàn)榇疟P容量或者是帶寬的原因拒絕文件的上載,事實(shí)上那個(gè)大文件還是被上載了(盡管它們立即又被刪除了),這對(duì)于你來說也許是個(gè)問題。
更好的方法是,你可以提前告訴PHP你希望可以接受的文件的大小的上限。有兩個(gè)方法。第一個(gè)是調(diào)整你的php.ini文件中的upload_max_filesize設(shè)置。默認(rèn)值是2MB,所以如果你想要接受更大的文件,你需要立即改變這個(gè)值。
第二個(gè)方法是在你的表單中包含一個(gè)隱含INPUT域,它的名字是MAX_FILE_SIZE,在其中你可以定義你所能接受的最大的文件的大小。出于安全的原因,這個(gè)值不能超過你的php.ini文件中的upload_max_filesize的設(shè)置,但是它提供了一種方法在不同頁(yè)面中各自定義上載文件大小的上限。例如,下面的表單,只允許我們最大上載1K字節(jié)的文件(1024字節(jié)):
<FORM ACTION="fileupload.php" METHOD=POST
ENCTYPE="multipart/form-data">
<P>Select file to upload:
<INPUT TYPE=FILE NAME="uploadedfile"></P>
<P><INPUT TYPE=SUBMIT NAME="submit" VALUE="Submit"></P>
<INPUT TYPE=HIDDEN NAME=MAX_FILE_SIZE VALUE=1024>
</FORM>
指定唯一的文件名
正如我們前面提到的,要保存一個(gè)上載的文件,我們需要將它拷貝到另一個(gè)目錄是保存。當(dāng)我們從$uploadedfile_name獲取每個(gè)上載的文件的文件名時(shí),我們不能保證不會(huì)上傳兩個(gè)同名的文件。在這種情況下,使用它的原名存儲(chǔ)文件將導(dǎo)致新上載的文件覆蓋原來上載的文件。
因?yàn)檫@個(gè)原因,你通過想要采用一種方法對(duì)所有上載的文件指定一個(gè)唯一的文件名。使用系統(tǒng)時(shí)間(它可以通過使用PHP的time函數(shù)獲得),我們可以容易地取得一個(gè)其于從1/1/1970到目前的秒數(shù)的名字。但是如果兩個(gè)文件恰巧在同一秒同時(shí)被上載呢?這了防止這個(gè)問題,我們?cè)谖募型瑫r(shí)使用客戶端的IP地址(由PHP自動(dòng)存儲(chǔ)在$REMOTE_HOST中)。因?yàn)槲覀儾惶赡芡幻霃耐粋(gè)IP地址接受兩個(gè)文件,這個(gè)方案應(yīng)該是可行的。
// Pick a file extension
if ( "image/pjpeg" == $uploadedfile_type )
$extension = ".jpg";
else
$extension = ".gif";
// The complete path/filename
$filename = "C:Uploads" . time() .
$REMOTE_HOST . $extension;
// Copy the file
if (copy($uploadedfile, $filename)) {
echo("<P>File stored successfully as $filename.");
} else {
echo("<P>Could not save file as $filename!");
}
請(qǐng)注意如果是在Windows下,我們必須在路徑中使用雙反斜杠(),因?yàn)榉葱备苡脕碓赑HP文本字符串中表示特殊字符。而在UNIX下,我們只要象通常那樣使用一個(gè)斜杠(/)。
將上載的文件保存到數(shù)據(jù)庫(kù)中
我們已經(jīng)建立了一個(gè)訪問者可以上傳JPEG和GIF圖象,并將其存儲(chǔ)在我們的服務(wù)器上的系統(tǒng),但是為什么不是這個(gè)教程所介紹的數(shù)據(jù)庫(kù)驅(qū)動(dòng)呢?如果我們這個(gè)系統(tǒng)就這樣維持現(xiàn)狀不變,將不得不有人負(fù)責(zé)從文件夾中收集提交的圖象并手工將其添加到Web站點(diǎn)!回頭看看第七章,在那里我們開發(fā)了一個(gè)系統(tǒng)使得站點(diǎn)的訪問者可以提交笑話將將其存到數(shù)據(jù)庫(kù)中,以備管理者通過,我們知道這是一種更好的方法!
MySQL有一些列類型可以用來存儲(chǔ)二進(jìn)制數(shù)據(jù)。在數(shù)據(jù)庫(kù)術(shù)語中,這些列類型可以存儲(chǔ)BLOB(二進(jìn)制大對(duì)象)。然而,在一個(gè)關(guān)系型數(shù)據(jù)庫(kù)中儲(chǔ)存大的文件并不是一個(gè)好主意。盡管將所有數(shù)據(jù)放在一個(gè)地方會(huì)給我們帶來方便,但是大的文件導(dǎo)致大的數(shù)據(jù)庫(kù),而大的數(shù)據(jù)庫(kù)會(huì)導(dǎo)致性能的降低和太大的備份文件。
通常最好的選擇是將文件名存儲(chǔ)在數(shù)據(jù)庫(kù)中。只要你記得在數(shù)據(jù)中刪除記錄時(shí)刪除相應(yīng)的文件就可以了。
PHP中的Email
Email在Internet上有著強(qiáng)大的影響力。不論你是想要給你的用戶提供一個(gè)"what's new"周刊,還是考慮一個(gè)途徑讓你的用戶可以獲得丟失的口令,你都會(huì)用到email。PHP便得使用email非常的簡(jiǎn)單,你只需要簡(jiǎn)單地調(diào)用mail函數(shù)就可以發(fā)出信息。
在你使用mail函數(shù)發(fā)送email之前,你首先必須設(shè)置PHP的與email相關(guān)的選項(xiàng)。這兒是在Windows下的php.ini文件中的相關(guān)行:
[mail function]
SMTP = localhost ;僅對(duì)win32有效
sendmail_from = me@localhost.com ;僅對(duì)win32有效
;sendmail_path = ;僅對(duì)unix有效 ...
取決于你使用的是Windows還是UNIX,PHP會(huì)通過一個(gè)SMTP服務(wù)器或一個(gè)本地的sendmail系統(tǒng)發(fā)送email。對(duì)這些的設(shè)置不是本教程的討論范圍,你可以從其它地方找到有關(guān)這二者的大量信息。然而,如果你是在Windows上運(yùn)行,有可能你的ISP已經(jīng)為你提供了一個(gè)SMTP服務(wù)器。這也就是你發(fā)送信息設(shè)置你的email程序所用的服務(wù)器。將SMTP設(shè)置為那個(gè)服務(wù)器的主機(jī)名或IP地址。
sendmail_from會(huì)設(shè)置你的emails發(fā)出時(shí)默認(rèn)的發(fā)出的email地址。如果你正在管理這個(gè)服務(wù)器,你可以將你的email地址放在這兒。
最后,sendmail_path在UNIX下將不會(huì)被注釋(也就是說,刪除這一行前面的分號(hào)),你需要將其設(shè)置為你的系統(tǒng)上的sendmail程序的路徑和文件名。在Linux下,這通常是指/usr/sbin/sendmail。
做好這些配置后重新啟動(dòng)你的Web服務(wù)器,PHP將具有全部的email功能,F(xiàn)在在PHP中發(fā)送一個(gè)email是相當(dāng)容易的:
mail("to-address@somewhere.com", "Message Subject", "This is the body of the message.");
要發(fā)送給多個(gè)收件人只需要將多個(gè)地址用逗號(hào)分開:
mail("to1@mail.net, to2@mail.net, ...", "Message Subject", "Message body");
在標(biāo)題中指定From:或者Reply-To:的地址也非常簡(jiǎn)單。將其作為第四個(gè)參數(shù)帶入,其中以回車-換行符分隔:
mail("to@mail.net", "Message Subject", "Message body", "From: webmaster@host.com Reply-to:admin@host.com");
與一個(gè)數(shù)據(jù)庫(kù)相結(jié)合,一個(gè)郵件列表變得非常容易管理!只需要從數(shù)據(jù)庫(kù)中取出地址列表并使用mail函數(shù)發(fā)送信息就行了。個(gè)人化的信息也非常簡(jiǎn)單。參看下面的例子:
// Retrieve $email and $password from the database based
// on the $username provided in a form.
mail($email, "Your Password",
"Hi there!
You just filled out a form on our Web site
indicating that you had lost your password.
As requested, we are sending it to you by
email.
username: $username
password: $password
Please record this information in a safe
place so you have it on hand for your next
visit to pingpongballs.com!
-The Webmaster.
");
如果你在在UNIX下運(yùn)行,而且你沒有一個(gè)本地的sendmail系統(tǒng)可以發(fā)送email,這也不要緊。PHP具有完善的TCP/IP網(wǎng)絡(luò)性能,如果需要,你可以連接到一個(gè)SMTP服務(wù)器以發(fā)送信息。同樣的,如果你需要在發(fā)出的信息中包含附件,PHP也可以實(shí)現(xiàn)這種功能。
不幸的是,內(nèi)置的mail函數(shù)不支持這些特征,如果你需要它們,你將不得不從頭編寫你自己的email函數(shù)。WROX Press編寫的"專業(yè)PHP程序設(shè)計(jì)"已經(jīng)為你完成了這些工作,在該書的第17章你可以找到全部的代碼。盡管這是我高度推薦的一本好書(見我的回顧),但是如果你只是想獲得這項(xiàng)功能,你也可以不購(gòu)買這本書,這個(gè)源代碼你可以從WROX的網(wǎng)站自由地下載。
除了這兩個(gè)小問題外,PHP的內(nèi)置mail函數(shù)為你的Web頁(yè)面發(fā)送email信息提供了令人難以相信的方便。