Học PHP Qua Các Ví Dụ



Phần I : Cơ Bản Về PHP
BÀI 1: Giới thiệu - Các bước chuẩn bị cần thiết

CÀI ĐẶT PHP

Để thực hành các ví dụ trong loạt bài hướng dẫn bạn cần phải cài đặt sẵn PHP trong máy của bạn, hoặc bạn phải có 1 website/hosting hỗ trợ PHP. Bạn có thể tham khảo một số bài viết hướng dẫn cài đặt PHP tại các địa chỉ:
- http://www.diendantinhoc.net/?articl...49&cat=web_php
- http://www.diendantinhoc.net/tute/hethong/apache-gd-mysql-phpinux/
- http://www.diendantinhoc.net/tute/he...ysql-php-perl/
- http://www.diendantinhoc.net/tute/he...-MySQL-duyson/
Các ví dụ trong bài viết sẽ được viết và chạy test trên PHP version 4.3.x trên các hệ thống:
- Windows 2k Pro/Server, XP Pro, Webserver IIS.
- Linux Redhat 9, Webserver Apache 2.0.x


CHỌN 1 CHƯƠNG TRÌNH SOẠN THẢO PHP


Để soạn thảo các mã nguồn chương trình PHP, bạn cần có một chương trình soạn thảo văn bản.
Trên Linux bạn có thể dùng vi/vim, gvim, kwrite...
Trên Windows bạn có thể dùng GVim for Windows, EditPlus, EmEditor, Dreamweaver MX...

Nếu bí quá không có gì xài, bạn xài tạm Notepad của Windows cũng được luôn. Nhưng bạn đừng lo, ở đây có khá nhiều chương trình soạn thảo PHP miễn phí: http://www.freeprogrammingresources.com/phpide.html.

Các ví dụ trong bài viết sẽ được soạn thảo trên vi/vim, kwrite trên Linux hoặc EditPlus và EmEditor trên Windows.
Theo kinh nghiệm cá nhân của tôi, nếu bạn soạn thảo mã nguồn PHP trên Windows thì EditPlus và EmEditor là 2 ứng cử viên xuất sắc: nhỏ gọn, nhiều chức năng tiện lợi. EditPlus có trội hơn EmEditor một chút về mặt tiện ích, nhưng lại khá bất tiện nếu như bạn muốn gõ tiếng Việt Unicode trong mã nguồn PHP. EmEditor hỗ trợ tiếng Việt Unicode khá tốt, nhưng mỗi file lại được mở trong 1 cửa sổ riêng, hơi choáng chỗ nếu như bạn phải soạn thảo nhiều file cùng một lúc.


LƯU FILE VỚI TIẾNG VIỆT UNICODE


Nếu bạn sử dụng tiếng Việt Unicode trong chương trình, bạn nên lưu file với encoding UTF-8, và khi lưu nhớ bỏ tuỳ chọn Save BOM Signature. Nếu không, 3 ký tự đánh dấu sẽ được tự động chèn vào đầu file mã nguồn PHP. 3 ký tự này nhiều chương trình soạn thảo văn bản hỗ trợ Unicode sẽ không hiển thị ra màn hình khi bạn mở file, nên bạn sẽ không biết là có 3 ký tự này ở đầu file, do đó có thể xảy ra một số lỗi không lường được.

Nếu bạn dùng EmEditor, khi bạn lưu file lần đầu tiên, hoặc lúc bạn Save as, bạn chỉ cần chọn Code Page là UTF-8 và bỏ chọn mục
Add a Unicode Signature (BOM) đi là được. Từ lần save thứ 2 trở đi, hoặc khi bạn mở file đã được save rồi thì bạn không cần phải chọn lại nữa.

Nếu bạn quên không bỏ mục chọn Add a Unicode Signature (BOM), 3 ký tự đánh dấu sẽ được tự động chèn vào đầu file, và hậu quả có thể là như thế này:
Hoặc có thể còn tệ hại hơn thế:
Nếu bạn mở file ra xem bằng một chương trình không hỗ trợ Unicode, bạn sẽ thấy có 3 ký tự ở ngay đầu file:
Cho nên bạn hãy nhớ bỏ 3 ký tự đánh dấu BOM ở đầu file khi lưu ở bảng mã Unicode.
CHỌN 1 VĂN PHONG MÃ NGUỒN
Chọn một văn phong nhất quán trong soạn thảo mã nguồn sẽ giúp code của bạn sáng sủa, dễ đọc trên nhiều môi trường khác nhau; và như vậy cũng phần nào giúp bạn tránh gặp lỗi và dễ sửa lỗi trong chương trình.

Bạn có thể áp dụng các qui tắc sau trong văn phong soạn thảo mã nguồn của bạn:
- Dùng ký tự tab khi cần thụt đầu dòng, đặt tab-stop bằng 4 space.
- Hàm/Thủ tục con:
/*
Chú thích về funcA, cách sử dụng, danh sách tham số, kết quả trả về...
*/
function funcA(tham số) {
...
} //end funcA
- Lệnh if...else:
if ( điều kiện ) {
...
} else {
...
} //end if
- Lệnh for, while:
for ( ... ) {
...
} //end for

while ( điều kiện ) {
...
} //end whil
e


Ngoài ra có một số văn phong và qui tắc mà bạn nên áp dụng:

bắt đầu chương trình bằng <?php, tuyệt đối tránh sử dụng <? vì nhiều server không hiểu/hỗ trợ <? nên rất có thể chương trình của bạn ở server này thì chạy, đem qua server khác thì "im re".
viết chương trình trong điều kiện môi trường safe_mode=On, register_globals=Off, display_errors=Off và trạng thái của magic_quotes_gpc là không xác định. Vì đây thường là các cài đặt mặc định của nhiều server, bạn nên tập viết chương trình trong điều kiện môi trường như vậy sẽ giúp cho chương trình của bạn tránh khỏi nhiều phiền hà khi chuyển từ server này sang server khác.
Nếu bạn nằm quyền điều khiển server (VD bạn cài trên máy ở nhà để viết chương trình, bạn hãy nên chỉnh cấu hình file php.ini cho đồng bộ với đa số các server hosting khác:

safe_mode=On: bật chế độ safe_mode sẽ làm cho hệ thống an toàn hơn đôi chút.
register_globals=Off: tắt chế độ register_globals bạn sẽ không còn sử dụng trục tiếp được các biến được truyền thẳng cho chương trình. Ví dụ: script của bạn được truy cập qua địa chỉ http://domain/script.php?test=123. Với register_globals=Off, chương trình của bạn không thể trực tiếp truy cập biến $test để lấy giá trị 123 mà cần phải truy cập qua $_GET['test']. Bạn đừng lo, register_globals=Off sẽ làm chương trình được nạp nhanh hơn và an toàn hơn.

display_errors=Off: tắt chế độ hiển thị thông tin về lỗi ra trình duyệt. Như vậy mỗi khi có lỗi màn hình sẽ...trắng bóc. Tuy khó debug chương trình hơn 1 chút nhưng như vậy sẽ an toàn hơn vì khi có lỗi, các thông tin về server sẽ không bị lôi hết ra hiển thị trên trình duyệt.
log_errors=On: vì lỗi sẽ không được hiển thị ra màn hình nữa nên bạn cần phải log lại vào file để sau này còn biết đường mà debug.
magic_quotes_gpc=Off: tắt magic_quotes sẽ làm chương trình được nạp nhanh hơn.
error_log=C:\php\error_log: chỉ định tên file sẽ dùng để lưu trữ error log. Bạn có thể tham khảo file php.ini-recommended (nằm trong file Zip khi bạn download PHP) để xem cấu hình gợi ý do chính nhóm phát triển PHP đề xuất.
BÀI 2: Sử dụng CSDL MySQL

GIỚI THIỆU

CSDL là 1 phần quan trọng không thể thể thiếu được trong các ứng dụng web chuyên nghiệp. MySQL từ lâu đã là hệ CSDL được dùng phổ biến nhất với PHP vì tính gọn nhẹ, nhanh, miễn phí và được PHP hỗ trợ sẵn. Trong bài viết này chúng ta sẽ tìm hiểu cách kết nối vào CSDL MySQL, truy cập và lưu trữ dữ liệu với PHP.

Để thực hành các ví dụ trong bài viết này, bạn cần phải cài MySQL vào máy của bạn. Bạn có thể tham khảo bài viết cài đặt MySQL Server 4.1 trên Windows ở địa chỉ này: http://www.diendantinhoc.net/?cat=da...cle=41cca76d6c. Xem như bạn đã cài đặt MySQL lên cùng server với PHP account để kết nối vào MySQL Serverusernameroot và mật mã (password) là rỗng.

Ngoài ra, bài viết này giới thiệu về cách làm việc với CSDL MySQL trong PHP, cho nên xem như là bạn đã có một số kiến thức căn bản về MySQL và ngôn ngữ SQL. Bạn có thể tham khảo bài viết Tự học về ngôn ngữ SQL ở địa chỉ này: http://www.diendantinhoc.net/tute/sql/tuhocsql/.


KẾT NỐI VÀO MYSQL SERVER

PHP cung cấp hàm mysql_connect để kết nối vèo MySQL server. Cú pháp của hàm này như sau:

mysql_connect($server_address, $username, $password)
$server_address là địa chỉ của MySQL server, có thể là domain name hoặc IP address, các ví dụ trong bài viết này sẽ dùng giá trị "localhost" cho $server_address.
$username là tên account dùng để login vào MySQL server, các ví dụ trong bài viết sẽ sử dụng giá trị "root" cho $username.
$password là mật mã để kết nối vào MySQL server, các ví dụ trong bài viết sẽ sử dụng chuỗi rỗng "" làm mật mã.
Hàm mysql_connect sẽ trả về 1 kết nối đến MySQL server nếu như quá trình kết nối thành công, hoặc trả về giá trị FALSE nếu như kết nối không được.

Để đóng kết nối tới MySQL server, PHP cung cấp hàm mysql_close. Đoạn mã sau ví dụ quá trình kết nối vào MySQL server và đóng kết nối.

<?php
$SERVER = "localhost";
$USERNAME = "root";
$PASSWORD = "";

$conn = mysql_connect($SERVER, $USERNAME, $PASSWORD);
if ( !$conn ) {
//Không kết nối được, thoát ra và báo lỗi
die("không nết nối được vào MySQL server");
} //end if

//đóng kết nối
mysql_close($conn);
?>



CHỌN CSDL ĐỂ LÀM VIỆC

Sau khi connet vào MySQL server, thao tác tiếp theo là chọn CSDL để làm việc. PHP cung cấp cho ta hàm mysql_select_db để làm việc này. Cú pháp của hàm này như sau:

mysql_select_db($db_name[, $conn])
Với $db_name là tên CSDL cần chọn, $conn là kết nối được thực hiện qua lệnh mysql_connect. Các ví dụ trong bài viết này sẽ sử dụng CSDL có tên là test:
<?php
$SERVER = "localhost";
$USERNAME = "root";
$PASSWORD = "";
$DBNAME = "test";

$conn = mysql_connect($SERVER, $USERNAME, $PASSWORD);
if ( !$conn ) {
//Không kết nối được, thoát ra và báo lỗi
die("không nết nối được vào MySQL server");
} //end if

//chọn CSDL để làm việc
mysql_select_db($DBNAME, $conn);

//đóng kết nối
mysql_close($conn);
?>

THỰC THI 1 CÂU LỆNH SELECT VÀ LẤY KẾT QUẢ TRẢ VỀ

PHP cung cấp cho ta 3 hàm hữu dụng để thực hiện công việc này:

$result = mysql_query($sql, $conn): thực hiện câu lệnh SQL được cung cấp qua tham số $sql và trả về 1 kết quả kiểu $result (hàm này trả về FALSE nếu như câu lệnh thực hiện không thành công).
mysql_num_rows($result): hàm này trả về số lượng row lấy được qua câu lệnh SELECT (được thực thi bởi hàm mysql_query) trước đó.
$row = mysql_fetch_row($result), $row = mysql_fetch_assoc($result): trả về dòng kết quả hiện thời của câu lệnh select và chuyển con trỏ tới dòng tiếp theo (như vậy lệnh gọi mysql_fetch_row hoặc mysql_fetch_assoc tiếp đó sẽ trả về dòng tiếp theo); hoặc giá trị FALSE nếu như không còn dòng nào để trả về nữa. Kết quả trả về từ 2 hàm này là 1 array.

mysql_error($conn): trả về thông báo lỗi của MySQL server nếu như một lệnh trước đó có lỗi.
Để hiểu rõ hơn công dụng của các hàm trên, đồng thời phân biệt sự khác nhau giữa 2 hàm mysql_fetch_rowmysql_fetch_assoc, ta cung xem xét các ví dụ sau.

Các ví dụ của ta sẽ sử dụng table có tên là member với các trường và dữ liệu như sau: username password
abc 123
def 456


Ví dụ 1: dùng mysql_fetch_row()

<?php
$SERVER = "localhost";
$USERNAME = "root";
$PASSWORD = "";
$DBNAME = "test";

$conn = mysql_connect($SERVER, $USERNAME, $PASSWORD);
if ( !$conn ) {
//Không kết nối được, thoát ra và báo lỗi
die("không nết nối được vào MySQL server: ".mysql_error($conn));
} //end if

//chọn CSDL để làm việc
mysql_select_db($DBNAME, $conn)
or die("Không thể chọn được CSDL: ".mysql_error($conn));

$sql = "SELECT * FROM member";
$result = mysql_query($sql, $conn);
if ( !$result )
die("Không thể thực hiện được câu lệnh SQL: ".mysql_error($conn));

echo "Số lượng row tìm được: ".mysql_num_rows($result)."<br>\n";

while ( $row = mysql_fetch_row($result) ) {
echo "Username = ".$row[0]."<br>\n";
echo "Password = ".$row[1]."<br>\n";
} //end while
//nên luôn giải phóng bộ nhớ sau khi lấy hết các row trả về từ câu lệnh SELECT
mysql_free_result($result);

//đóng kết nối
mysql_close($conn);

?>
Hàm mysql_fetch_row() sẽ trả về 1 array mà phần tử thứ [0] sẽ tương ứng với cột đầu tiên của table, phần tử thứ [1] sẽ tương ứng với cột thứ hai của table...Chương trình trên sẽ in ra ra 4 dòng:

Username = abc
Password = 123
Username = def
Password = 456

Ví dụ 2: dùng mysql_fetch_assoc()

<?php
$SERVER = "localhost";
$USERNAME = "root";
$PASSWORD = "";
$DBNAME = "test";

$conn = mysql_connect($SERVER, $USERNAME, $PASSWORD);
if ( !$conn ) {
//Không kết nối được, thoát ra và báo lỗi
die("không nết nối được vào MySQL server: ".mysql_error($conn));
} //end if

//chọn CSDL để làm việc
mysql_select_db($DBNAME, $conn)
or die("Không thể chọn được CSDL: ".mysql_error($conn));

$sql = "SELECT * FROM member";
$result = mysql_query($sql, $conn);
if ( !$result )
die("Không thể thực hiện được câu lệnh SQL: ".mysql_error($conn));

echo "Số lượng row tìm được: ".mysql_num_rows($result)."<br>\n";

while ( $row = mysql_fetch_assoc($result) ) {
echo "Username = ".$row['username']."<br>\n";
echo "Password = ".$row['password']."<br>\n";
} //end while
//nên luôn giải phóng bộ nhớ sau khi lấy hết các row trả về từ câu lệnh SELECT
mysql_free_result($result);

//đóng kết nối
mysql_close($conn);

?>
Hàm mysql_fetch_assoc() sẽ trả về 1 array mà các phần tử sẽ được truy cập qua tên, với tên được lấy từ tên các cột của table. Chương trình ví dụ 2 cũng sẽ in ra ra 4 dòng:

Username = abc
Password = 123
Username = def
Password = 456

THỰC THI 1 CÂU LỆNH UPDATE, INSERT hoặc DELETE

Hàm mysql_query cũng được dùng để thực thi các câu lệnh DELETE, INSERT hoặc UPDATE, nhưng lúc này hàm sẽ trả về TRUE nếu câu lệnh thực hiện thành công và FALSE trong trường hợp ngược lại. Để lấy số lượng các row được chèn với lệnh INSERT hoặc bị thay đổi bởi lệnh UPDATE, PHP cung cấp cho ta hàm mysql_affected_rows. Ta hãy xem ví dụ sau:

<?php
$SERVER = "localhost";
$USERNAME = "root";
$PASSWORD = "";
$DBNAME = "test";

$conn = mysql_connect($SERVER, $USERNAME, $PASSWORD);
if ( !$conn ) {
//Không kết nối được, thoát ra và báo lỗi
die("không nết nối được vào MySQL server: ".mysql_error($conn));
} //end if

//chọn CSDL để làm việc
mysql_select_db($DBNAME, $conn)
or die("Không thể chọn được CSDL: ".mysql_error($conn));

$sql = "INSERT INTO member (username, password) VALUES ('xyz', '000')";
$result = mysql_query($sql, $conn); //chèn thêm 1 dòng vào table
if ( !$result )
die("Không thể thực hiện được câu lệnh SQL: ".mysql_error($conn));

echo "Số lượng row được chèn: ".mysql_affected_rows($conn)."<br>\n";
//ta chỉ chèn 1 dòng nên hàm mysql_affected_rows sẽ trả về 1

$sql = "UPDATE member SET password='111' WHERE username='xyz'";
$result = mysql_query($sql, $conn); //đổi password của accoutn xyz
if ( !$result )
die("Không thể thực hiện được câu lệnh SQL: ".mysql_error($conn));

echo "Số lượng row được thay đổi: ".mysql_affected_rows($conn)."<br>\n";
//ta thay đổi 1 dòng nên hàm mysql_affected_rows sẽ trả về 1

$sql = "DELETE FROM member";
$result = mysql_query($sql, $conn); //xoá hết tất cả các account
if ( !$result )
die("Không thể thực hiện được câu lệnh SQL: ".mysql_error($conn));

echo "Số lượng row được xoá: ".mysql_affected_rows($conn)."<br>\n";
//ta xoá tất cả 3 dòng nên hàm mysql_affected_rows sẽ trả về 3

//đóng kết nối
mysql_close($conn);

LƯU Ý VẤN ĐỀ BẢO MẬT VỚI LỖI SQL INJECTION

Nếu bạn cần cung cấp 1 tham số cho câu lệnh SQL, nhất là các tham số từ trình duyệt do người dùng nhập vào, bạn hãy lưu ý đề phòng lỗi bảo mật SQL Injection. Giả sử bạn muốn thay đổi mật mã của account xyz, mật mã mới được người dùng nhập vào và lưu vào trong biến $newPwd, đoạn mã đổi password có thể tương tự như sau:

<?php
//...
$sql = "UPDATE member SET password='$newPwd' WHERE username='xyz'";
mysql_query($sql, $conn);
//...
?>


Giả sử người dùng nhập vào mật mã mới là zzz, câu lệnh SQL sẽ trở thành
UPDATE member SET password='zzz' WHERE username='xyz'
Hoàn toàn hợp lệ và đúng đắn, không có gì phải thắc mắc hết.

Nhưng giả sử người dùng nhập vào mật mã mới là zzz'#, câu lệnh SQL sẽ trở thành
UPDATE member SET password='zzz'#' WHERE username='xyz'
Nhưng gì phía sau ký tự # sẽ được MySQL xem là chú thích và sẽ bỏ qua, như vậy câu lệnh SQL của chúng ta trên thực tế sẽ tương đương với:
UPDATE member SET password='zzz'
Vậy là xong! Sau khi chạy câu lệnh này, password của tất cả các account đều là zzz hết, tất hiên là account admin cũng sẽ bị đổi password thành zzz và lúc này hậu quả tiếp theo ra sao chắc bạn cũng đã rõ!

Để tránh bị SQL injection, khi đưa các tham số vào câu lệnh SQL, bạn nên luôn nhớ và áp dụng 2 điều sau:

Nếu tham số là số (số nguyên hoặc số thực), cộng thêm 0 vào tham số trước khi đưa vào câu lệnh SQL. Tức là:
$thamso+=0;
$sql = "...$thamso...";
Nếu tham số là chuỗi, sử dụng hàm mysql_real_escape_string trước khi đưa tham số vào câu lệnh SQL. Tức là:
$thamso = mysql_real_escape_string($thamso, $conn);
$sql = "...'$thamso'...";
?>


TRƯỚC KHI KẾT THÚC

Đến đây, bạn đã nắm được các bước cơ bản để làm việc với CSDL MySQL Server:
- Kết nối vào MySQL Server với server address, username và password
- Chọn CSDL để làm việc
- Thao tác với CSDL, Thực thi các lệnh SQL...
- Đóng kết nối khi kết thúc

Trong thực tế, đôi lúc chương trình của bạn cần thực hiện thêm nhiều thao tác nâng cao hơn thế, lúc này bạn đừng quên tham khảo MySQL Manual mục các hàm về MySQL.


CÁC HÀM PHP ĐƯỢC DÙNG TRONG VÍ DỤ

die: in ra câu thông báo và kết thúc chương trình.
mysql_connect: kết nối vào CSDL MySQL Server.
mysql_close: đóng kết nối đã được thiết lập trước đó.
mysql_error: lấy câu thông báo lỗi (nếu có) trả về từ MySQL Server.
mysql_fetch_row: lấy 1 row kết quả từ câu lệnh SELECT.
mysql_fetch_assoc: lấy 1 row kết quả từ câu lệnh SELECT.
mysql_free_result: giải phóng bộ nhớ sau khi lấy kết quả trả về từ câu lệnh SELECT.
mysql_query: thực thi 1 câu lệnh SQL.
mysql_select_db: chọn CSDL để làm việc.
mysql_real_escape_string: escale 1 chuỗi trước khi chèn vào câu lệnh SQL.


BAI 3:Gởi email text, HTML và tiếng Việt Unicode

HÀM MAIL() CỦA PHP

Send email trong PHP rất đơn giản, ta chỉ cần dùng duy nhất 1 hàm mail() được PHP cung cấp sẵn là đủ. Cú pháp của hàm mail() như sau:

bool mail(string to, string subject, string message[, string additional_headers])

Các tham số của hàm mail() có ý nghĩa như sau:

to - địa chỉ người nhận email, là 1 string. Các giá trị hợp lệ của tham số to có dạng như sau:
'user@domain.com'
'user1@domain.com, user2@domain.com'
'User Name <user@example.com>'
'User Name 1 <user1@example.com>, User Name 2 <user2@example.com>'
'User Name 1 <user@example.com>, user2@domain.com, User Name 3 <user3@example.com>'


subject - tiêu đề của email, là 1 string. Tiêu đề của email không được chứa ký tự xuống dòng (\r hoặc \n).
message - nội dung email, là 1 string. Nội dung email có thể do nhiều dòng ghép lại với nhau, mỗi dòng được phân cách bằng ký tự \n.
additional_headers - (tuỳ chọn) dùng để chèn thêm các header vào email. Các header này không nằm trong phần nội dung của email mà dùng để quản lý việc gởi email (ví dụ chèn thêm các trường CC, BCC khi gởi email). Nhiều header có thể cùng ghép lại thành 1 string trong additional_headers, các header được phân cách nhau bằng chuỗi \r\n.

Hàm mail() sẽ trả về giá trị TRUE nếu như email được chấp nhận gởi đi và FALSE trong trường hợp ngược lại.
Lưu ý: Hàm mail() trả về giá trị TRUE không có nghĩa là email sẽ được gởi đến nơi nhận thành công. Giá trị TRUE được trả về chỉ có ý nghĩa là email được server chấp nhận gởi đi, còn khi nào thì email thực sự được gởi, gởi có tới nơi hay không thì chương trình không biết được (như vậy nếu bạn nhập sau địa chỉ người nhận ở tham số To thì sẽ không có cách nào chương trình biết được địa chỉ To có đúng hoặc tồn tại hay không!).


MỘT VÍ DỤ GỞI EMAIL VỚI HÀM MAIL()

Ta hãy tạo 1 chương trình PHP nhỏ đẻ gởi email như sau:

//nội dung file example1.php
<?php
$from = 'from@domain.com';
$to = 'to@domain.com';
$subject = 'Example 1: Send simple text email';
$message = 'A plain text email.';
$header = "From: $from\r\nReply-to: $from";

if ( mail($to, $subject, $message, $header) ) {
echo "Email sent to $to!";
} else {
echo "Error occured while sending email to $to!";
} //end if
?>

Lưu chương trình vào file example1.php và chạy thử. Nếu quá trình gởi email thành công, bạn sẽ nhận được câu thông báo "Email sent to to@domain.com!", nếu có lỗi bạn sẽ nhận được câu thông báo "Error occured while sending email to to@domain.com!"

Lưu ý:

Nhớ thay to@domain.comfrom@domain.com thành địa chỉ email của bạn trước khi chạy thử chương trình!
Có thể phải mất tới 5-10 phút email mới tới nơi, và bạn nhớ kiểm tra thư mục Bulk email vì nhiều khi email gởi tới bị để trong mục Bulk.

Trong cú pháp của hàm mail(), tham số additional_headers là tuỳ chọn, tức là bỏ qua cũng được. Tuy nhiên, trên thực tế bạn nên cung cấp tham số additional_headers cho hàm mail() với ít nhất 2 header From và Reply-to. Lý do thì có nhiều, một trong số các lý do phải cung cấp header From và Reply-to là vì nếu phần địa chỉ người gởi (From) là rỗng, các chương trình lọc email có thể cho rằng đây là emai spam và thế là email được tự động chuyển sang "Thùng rác"!.


GỞI HTML EMAIL VỚI HÀM MAIL()

Chúng ta đã gởi được email text với hàm mail(), nhưng nhiều lúc ta cần "trang điểm" màu mè cho nội dung email được bắt mắt hơn. Thử viết 1 chương trình nhỏ gởi email HTML:

//nội dung file example2.php
<?php
$from = 'from@domain.com';
$to = 'to@domain.com';
$subject = 'Example 2: Try a simple HTML email';
$message = 'A HTML email: <b>bold</b>, <i>italic</i>, <u>underline</u>.';
$header = "From: $from\r\nReply-to: $from";

if ( mail($to, $subject, $message, $header) ) {
echo "Email sent to $to!";
} else {
echo "Error occured while sending email to $to!";
} //end if
?>

Lưu chương trình vào file example2.php và chạy thử. Email gởi thành công, tới nơi an toàn, nhưng khi mở ra xem thì nội dung email lại như vầy:
A HTML email: <b>bold</b>, <i>italic</i>, <u>underline</u>.
Thế có tức không?

Thực ra bạn chỉ cần sửa lại chương trình "1 chút xíu" là đâu vào đấy ngay:

//nội dung file example3.php
<?php
$from = 'from@domain.com';
$to = 'to@domain.com';
$subject = 'Example 3: Send HTML email';
$message = 'A HTML email: <b>bold</b>, <i>italic</i>, <u>underline</u>.';
$header = "Content-type: text/html\r\nFrom: $from\r\nReply-to: $from";

if ( mail($to, $subject, $message, $header) ) {
echo "Email sent to $to!";
} else {
echo "Error occured while sending email to $to!";
} //end if
?>

Lưu chương trình vào file example3.php và chạy thử. Lần này thì nội dung email nhận được như mong muốn:
A HTML email: bold, italic, underline..

Như vậy bạn sẽ nhận thấy rằng gởi HTML email cũng đơn giản không khác gì gởi text email thông thường. Chỉ cần thêm 1 header Content-type: text/html là nội dung email sẽ được hiểu là HTML ngay.

Lưu ý: Riêng phần tiêu đề (subject) của email vẫn phải là text thông thường! Bạn không thể sử dụng mã HTML trong phần tiêu đề (subject) của email được. Vì thực chất phần tiêu đề của email cũng là 1 header đặt biệc chứ không nằm trong phần nội dung chính của email.

GỞI EMAIL VỚI TIẾNG VIỆT UNICODE

Gởi email tiếng Việt Unicode cũng y chang như gởi email HTML vì bản thân HTML đã hỗ trợ Unicode rồi, ta chỉ cần chú ý đặt charset của email cho đúng là được:

//nội dung file example3.php
<?php
$from = 'from@domain.com';
$to = 'to@domain.com';
$subject = 'Example 4: Send Unicode email';
$message = 'Email tiếng Việt Unicode: <b>in đậm</b>, <i>in nghiêng</i>, <u>gạch chân</u>.';
$header = "Content-type: text/html; charset=utf-8\r\nFrom: $from\r\nReply-to: $from";

if ( mail($to, $subject, $message, $header) ) {
echo "Email sent to $to!";
} else {
echo "Error occured while sending email to $to!";
} //end if
?>
Lưu chương trình vào file example4.php và chạy thử: email gởi thành công, tới nơi an toàn và khi đọc thì hiển thị...tiếng Ả-rập!!! Có gì sai ở đây chăng?

Thực ra không có gì phải lo lắng cả. Khi gởi email tiếng Việt Unicode bạn cần chú ý một số điểm sau:

Đặt charset cho email. Charset này phải nên trùng với charset của file mã nguồn PHP và website của bạn. Trong ví dụ ở trên, ta đặt charset cho email là utf-8 thì bạn cũng phải lưu file example4.php lên đĩa với charset là utf-8. Bạn xem mục "LƯU FILE VỚI TIẾNG VIỆT UNICODE" ở bài viết Giới thiệu - Các bước chuẩn bị cần thiết để tìm hiểu thêm về lưu file mã nguồn PHP lên đĩa với charset utf-8.
Cho dù bạn đã đặt charset đúng rồi, vẫn có thể email không hiển thị đúng tiếng Việt khi đọc! Vì quyền hiển thị email như thế nào là do chương trình email client quyết định. Nếu bạn đọc email bằng Outlook chẳng hạn, thì Outlook sẽ tự động nhận biết charset của email và hiển thị đúng tiếng Việt. Nếu bạn đọc email trên web (như Yahoo, Hotmail...) thì có thể bạn phải tự tay chọn lại Encoding là utf-8 trên trình duyệt vì các web email như Yahoo, Hotmail sẽ dùng charset mặc định của mình và bỏ qua charset được thiết lập trong email.
Và tương tự như khi gởi HTML email, bạn đừng nên ghi tiếng Việt vào phần tiêu đề (subject) của email! Vì như đã nói ở phần trước, tiêu đề (subject) của email cũng là 1 header đặt biệc chứ không nằm trong phần nội dung chính của email.

MỘT SỐ LỖI HAY GẶP VỚI HÀM MAIL()

Email gởi không tới nơi
Hàm mail() trả về giá trị TRUE, địa chỉ đúng, nội dung đúng, nói chung mọi thứ đều đúng nhưng email chờ hoài không thấy tới! Kiểm tra Bulk email cũng không thấy luôn! Lỗi ở đâu hè? Nguyên nhân có thể xảy ra ở 3 chỗ:

mail server - server của bạn không cho phép account của bạn gởi email, hoặc giới hạn chỉ cho phép gởi số lượng giới hạn email trong 1 khoảng thời gian nào đó. Bạn cần hỏi người quản lý server của bạn về quyền gởi email với PHP trên server.
Nếu vấn đề không phải nằm ở chỗ server không cho phép bạn gởi email, hoặc chính bạn là người quản lý server thì bạn kiểm tra tiếp 1 trong 2 nguyên nhân sau:
*NIX server (server chạy các hệ điều hành UNIX hoặc Linux) - PHP mặc định sẽ sử dụng lệnh sendmail có sẵn trên hệ thống để gởi email (với tham số -t -i). Mặc định lệnh sendmail sẽ nằm trong /usr/sbin/sendmail hoặc trong 1 thư mục nào đó trong PATH của hệ thống. Nếu trên server của bạn lệnh sendmail nằm ở chỗ khác với thư mục mặc định (ví dụ trên server của bạn sendmail nằm ở /usr/local/secretbin/sendmail) thì bạn có thể làm như sau:
- Tìm và mở file php.ini ra.
- Tìm chuỗi "sendmail", bạn sẽ thấy 2 dòng tương tự như sau:
; For Unix only. You may supply arguments as well (default: "sendmail -t -i").
;sendmail_path =
- Bạn sửa lại 2 dòng đó như sau:
; For Unix only. You may supply arguments as well (default: "sendmail -t -i").
sendmail_path = /usr/local/secretbin/sendmail -t -i
Nhớ bỏ ký tự chấm phảy ( ở đầu dòng sendmail_path =!
Lưu ý: Cấu hình của PHP tới đây đã đúng và đủ để gởi email trên server *NIX. Tuy nhiên vẫn có thể server không gởi email được! Lỗi lúc này là do bản thân cấu hình của server hoặc quá trình cài đặt và cấu hình chương trình sendmail bị lỗi. Cách kiểm tra nguyên nhân và sửa lỗi nằm ngoài phạm vi của PHP, bạn có thể đặt câu hỏi lên diễn đàn CLB người sử dụng Linux để được giúp đỡ!
Windows server - Trên Windows, PHP cần phải dùng 1 SMTP server để gởi email. Bạn tìm và mở file php.ini ra, tìm chuỗi sendmail, gần đó bạn sẽ thấy vài dòng như sau:
[mail function]
; For Win32 only.
SMTP = localhost
smtp_port = 25;
Các dòng trên cho ta biết PHP sẽ dùng SMTP server localhost trên port 25 để gởi email. Rất có thể bạn chưa cài đặt và cấu hình đúng 1 SMTP trên localhost để PHP có thể sử dụng và gởi email. Bạn hãy tham khảo bài viết này để cài đặt 1 SMTP server trên localhost và dùng nó để gởi email với PHP.

Nội dung email bị đứt đoạn
Nếu bạn nhận được email nhưng nội dung bị đứt đoạn không đầy đủ thì có thể do 2 nguyên nhân sau:

Mã HTML trong email bị sai - Nếu bạn gởi email HTML thì bạn nên chú ý kiểm tra cú pháp HTML của nội dung email. Giả sử bạn định gởi email với nội dung:
<b>in đậm</b>
Nhưng chẳng may bạn ghi sai thành <b in đậm</b>
thì khi đọc, email sẽ hiển thị bị sai (có thể là mất chữ, đứt đoạn, v.v...).
Có 1 dòng chứa 1 dấu chấm (.) ở đầu - Khi bạn gởi email trên Windows dùng SMTP server, nếu nội dung email có 1 dòng nào đó chứa 1 dấu chấm (ký tự .) ở đầu dòng thì ký tự đó sẽ bị bỏ qua, dòng đó sẽ bị bỏ qua, hoặc toàn bộ nội dung email từ dòng đó trở đi sẽ bị bỏ qua. Lý do là vì dòng chứa 1 dấu chấm ở đầu chính là dấu hiệu báo cho SMTP server biết đã kết thúc nội dung của email. Để khắc phục, trước khi gởi email, ta thay thế dấu chấm ở đầu dòng bằng 2 dấu chấm:
$message = str_replace("\n.", "\n..", $message);
BÀI 4 :Làm việc với Cookie và Session

GIỚI THIỆU

Trong quá trình phát triển web, một nhu cầu rất thường xuyên nảy ra là làm cách nào truyền dữ liệu từ trang web này sang trang web khác? Tất nhiên ta có thể dùng form để submit và truyền data từ một trang web sang trang web tiếp theo qua phương thức POST hoặc truyền dữ liệu qua URL theo phương thức GET. Tuy nhiên, cách làm này không phải lúc nào cũng khả thi. Trong nhiều trường hợp, cách dùng form hoặc URL gây khá nhiều phiền phức hoặc hầu như không thể thực hiện được. Ví dụ: người dùng click lên 1 link trên trang web của bạn để đi sang 1 website khác, rồi click lên 1 link trên website kia để quay ngược về website của bạn.

Rất may mắn, vấn đề được giải quyết khá dễ dàng với sự trợ giúp của COOKIESESSION. Và còn hơn thế nữa, PHP cung cấp rất sẵn nhiều hàm và phương tiện để làm việc dễ dàng và hiệu quả với COOKIESESSION. Trong bài viết này chúng ta sẽ tìm hiểu về COOKIE, SESSION và cách sử dụng COOKIE, SESSION trong PHP.

COOKIE VÀ SESSION LÀ GÌ?

Cookie là 1 đoạn dữ liệu được truyền đến browser từ server, đoạn dữ liệu này sẽ được browser lưu trữ (trong memory hoặc trên đĩa) và sẽ gởi ngược lên lại server mỗi khi browser tải 1 trang web từ server [1].
Những thông tin được lưu trữ trong cookie hoàn toàn phụ thuộc vào website trên server. Mỗi website có thể lưu trữ những thông tin khác nhau trong cookie, ví dụ thời điểm lần cuối bạn ghé thăm website, đánh dấu bạn đã login hay chưa, v.v...
Cookie được tạo ra bởi website và gởi tới browser, do vậy 2 website khác nhau (cho dù cùng host trên 1 server) sẽ có 2 cookie khác nhau gởi tới browser. Ngoài ra, mỗi browser quản lý và lưu trữ cookie theo cách riêng của mình, cho nên 2 browser cùng truy cập vào 1 website sẽ nhận được 2 cookie khác nhau.

Session là khoảng thời gian người sử dụng giao tiếp với 1 ứng dụng. Session bắt đầu khi người sử dụng truy cập vào ứng dụng lần đầu tiên, và kết thúc khi người sử dụng thoát khỏi ứng dụng [2]. Mỗi session sẽ có một định danh (ID), 1 session khác nhau sẽ có 2 ID khác nhau. Trong ngữ cảnh ứng dụng web, website sẽ quyết định khi nào session bắt đầu và kết thúc.
Trong 1 session, website có thể lưu trữ một số thông tin như đánh dấu bạn đã login hay chưa, những bài viết nào bạn đã đọc qua, v.v...

Điểm giống và khác nhau giữa CookieSession
Cookie và Session đều có chung mục đích là lưu giữ data để truyền từ 1 trang web sang 1 trang web khác (trên cùng website). Nhưng phước thức lưu trữ và quản lý data của Cookie và Session có phần khác nhau.
Cookie sẽ được lưu trữ tại browser, do browser quản lý và browser sẽ tự động truyền cookie ngược lên server mỗi khi truy cập vào 1 trang web trên server.
Dữ liệu lưu trữ trong Session sẽ được ứng dụng quản lý, trong ngữ cảnh web, ứng dụng ở đây sẽ là website và webserver. Browser chỉ truyền ID của session lên server mỗi khi truy cập vào website trên server.

Tự liên hệ giữa Session và Cookie: Mỗi Session gắn với 1 định danh (ID). ID sẽ được tạo ra trên server khi session bắt đầu và được truyền cho browser. Sau đó browser sẽ truyền lại ID này lên server mỗi khi truy cập vào website. Như vậy ta có thể thấy rằng sẽ rất tiện nếu như Session ID được lưu trữ trong Cookie và được browser tự động truyền lên server mỗi khi truy cập vào website.

Sử dụng Cookie hay Session?
Sử dụng Session hoặc Cookie là tuỳ vào lựa chọn của Lập trình viên, tuy nhiên Session thường được ưa chuộng hơn Cookie vì một số lý do sau:


Trong một số trường hợp Cookie không sử dụng được. Có thể browser đã được thiết lập để không chấp nhận cookie, lúc đó session vẫn sử dụng được bằng cách truyền session ID giữa các trang web qua URL, ví dụ: script.php?session=abc123.
Lượng data truyền tải giữa browser và server: chỉ mỗi session ID được truyền giữa browser và server, data thực sự được website lưu trữ trên server.
Bảo mật: càng ít thông tin được truyền tải qua lại giữa browser và client càng tốt, và càng ít thông tin được lưu trữ tại client càng tốt.


SỬ DỤNG COOKIE TRONG PHP

Sử dụng Cookie trong PHP khá đơn giản. Để đặt (set) cookie, bạn chỉ cần sử dụng 1 lệnh gán đơn giản:
$_COOKIE[tên_cookie] = giá_trị;. Và để đọc (get) lại giá trị của cookie, bạn chỉ cần đọc biến $_COOKIE[tên_cookie].

Ta hãy xem ví dụ sau:

//nội dung file a.php
<?php
$t = time();
$_COOKIE['abc'] = time();
?>
<html>
<head><title>a.php</title></head>
<body>Giá trị của cookie đã được gán giá trị <?php echo $t; ?>. Click <a href="b.php">vào đây</a> để kiểm tra.</body>
</html>

//nội dung file b.php
<html>
<head><title>b.php</title></head>
<body>Giá trị của Cookie được gán trong <a href="a.php">file a.php</a>: <?php echo $_COOKIE['abc']; ?>.</body>
</html>


Lưu 2 đoạn mã trên vào 2 file a.php và b.php. Đầu tiên, dùng browser truy cập vào file a.php bạn sẽ thấy 1 màn hình tương tự như sau:

Giá trị của cookie đã được gán giá trị 1228758522. Click vào đây để kiểm tra.

Click vào link "vào đây" để truy cập qua file b.php bạn sẽ thấy 1 màn hình tương tự như sau:

Giá trị của Cookie được gán trong file a.php: 1228758522.


Lấy session ID: trong nhiều trường hợp, bạn cần lấy giá trị của session ID (để đếm số lượng session đã được tạo chẳng hạn), PHP cung cấp cho ta hàm session_id() để lấy giá trị hiện tại của session ID.
Lưu ý: hàm session_id() phải được gọi sau hàm session_start(). Ví dụ:

//khời tạo session
session_start();

//lấy sesion ID
$id = session_id();
echo "Session ID = ".$id;


MỘT SỐ LƯU Ý KHI SỬ DỤNG COOKIE VÀ SESSION

Các lệnh gán giá trị cho CookieSession phải nên được gọi trước khi bất cứ dữ liệu nào được xuất ra browser. Thậm chí 1 ký tự trắng ở đầu chương trình (có ký tự trắng trước <?php) cũng không được! Nếu bạn sử dụng Unicode trong chương trình, bạn cần chú ý ký tự BOM ở đầu file nguồn PHP. Xem thêm mục "LƯU FILE VỚI TIẾNG VIỆT UNICODE" trong phần "Giới thiệu - Các bước chuẩn bị cần thiết".
Lệnh session_start() phải được gọi ở đầu chương trình, trước tất cả các lệnh gán hoặc đọc session.
CÁC HÀM PHP ĐƯỢC DÙNG TRONG VÍ DỤ

session_start: khởi tạo session.
session_id: lấy giá trị hiện tại của session ID.

Bài 5: Upload File Lên Server


TẠO FORM ĐỂ UPLOAD FILE

Form để upload file cần thoã mãn các điều kiện sau:

methodPOST
enctypemultipart/form-data
Mã HTML của form sẽ từa tựa như sau:

<form method="POST" enctype="multipart/form-data" action="process_upload.php">
<input type="hidden" name="MAX_FILE_SIZE" value="30000">
<input type="file" name="file_upload" size="20">
<input type="submit" value="Upload">
</form>
Đoạn code trên sẽ tạo 1 form với 1 nút Browse... để ban chọn file cần upload, và 1 nút Upload để bạn submit form. Form sẽ được submit tới file process_upload.php nằm cùng thư mục với file chứa form.

Một số browser support MAX_FILE_SIZE sẽ kiểm tra dung lượng file trước khi form được submit, tuy nhiên không phải browser nào cũng vậy. Cho nên bạn đừng nên tin tưởng tuyệt đối vào server! Ở ví dụ trên, nếu browser hỗ trợ, nhưng file có dung lượng lớn hơn 30000 byte sẽ được browser thông báo lỗi khi submit form.


XỬ LÝ DỮ LIỆU ĐƯỢC SUBMIT LÊN SERVER

Bây giờ ta hãy xem xét tới phần xử lý dữ liệu được submit lên server trong file process_upload.php. PHP lưu thông tin về file được upload lên server trong biến global $_FILES. Với form ở ví dụ trên, PHP sẽ truyền cho script process_upload.php các thông tin sau:

$_FILES['file_upload']['name']: tên file gốc trên máy client. Tuỳ vào browser, tên file có thể được truyền lên server ở dạng C:\folder\filename.ext hoặc chỉ là filename.ext. Chương trình phải tự kiểm tra và trích ra tên file nếu cần thiết.
$_FILES['file_upload']['type']: kiểu của file, được lưu ở dạng MINE (Ví dụ: image/gif, audio/wav).
$_FILES['file_upload']['size']: dung lượng của file tính theo byte.
$_FILES['file_upload']['tmp_name']: sau khi upload, server sẽ lưu file vào một file tạm trên server, biến này cho ta biết đường dẫn và tên của file tạm đó. Chương trình sẽ đọc file tạm này để lấy nội dung của file được upload.
$_FILES['file_upload']['error']: mã lỗi, chương trình nên kiểm tra biến này để bảo đảm rằng quá trình upload không xảy ra lỗi.
UPLOAD_ERR_OK ( = 0 ) : không có lỗi, quá trình upload thành công.
UPLOAD_ERR_INI_SIZE ( = 1 ) : dung lượng file upload vượt quá giới hạn được chỉ định trong file php.ini.
UPLOAD_ERR_FORM_SIZE ( = 2 ) : dung lượng file upload vượt quá giới hạn được chỉnh định bởi MAX_FILE_SIZE.
UPLOAD_ERR_PARTIAL ( = 3 ) : file chỉ được upload 1 phần (có thể là do lỗi đường truyền trong quá trình upload).
UPLOAD_ERR_NO_FILE ( = 4 ) : không có file nào được upload (có thể là file ở client không tồn tại).
Khi đã có toàn bộ các thông tin cần thiết, xử lý file như thế nào là quyến định của bạn. Bạn có thể đọc nội dung của file và lưu vào database, hoặc di chuyển file và lưu vào thư mục upload của bạn. Sau đây là 1 ví dụ của file process_upload.php.

Đầu tiên, kiểm tra xem tác vụ có phải là upload hay không:

if ( $_SERVER["[COLOR="red"]REQUEST_METHOD
"] != "POST" ) {
//thông báo lỗi không phải là method POST
//và thoát
exit(-1);
} //end if
Tiếp theo kiểm tra xem quá trình upload có lỗi gì không:

if ( !isset($_FILES["file_upload"]["error"] ||
$_FILES["[COLOR="red"]file_upload[/COLOR]"]["error"] != 0 ) {
//thông báo lỗi dựa vào giá trị của $_FILES["[COLOR="red"]file_upload[/COLOR]"]["error"]
//và thoát
exit(-1);
} //end if


//ta cũng có thể kiểm tra xem dung lượng file có vượt quá giới hạn
//của chương trình hay không
if ( $_FILES["file_upload"]["size"] > $MAX_FILE_SIZE ) {
//thông báo lỗi
//và thoát
exit(-1);
}

Tách tên file từ client:

$temp = preg_split('/[\/\\\\]+/', $_FILES["file_upload"]["name"]);
$filename = $temp[count($temp)-1];

//ta cũng có thể kiểm tra phần mở rộng của file nếu cần thiết
if ( !preg_match('/\.(gif|jpg)$/i', $filename ) {
//thông báo lỗi file upload không phải là dạng GIF hoặc JPG
//và thoát
exit(-1);
} //end if

Và cuối cùng, lưu file được upload vào nơi cần thiết:

$upload_dir = "/home/nbthanh/public_html/uploads/";
$upload_file = $uploaddir . $filename;
if ( move_uploaded_file($_FILES["file_upload"]["tmp_name"], $upload_file) ) {
//file đã được upload và copy sang thư mục lưu trữ thành công
} else {
//có lỗi xảy ra
} //end if [/COLOR]


CÁC HÀM PHP ĐƯỢC DÙNG TRONG VÍ DỤ

exit: dừng/thoát chương trình ngay lập tức.
isset: kiểm tra xem biến có tồn tại hay không. Trong ví dụ của bài viết, ta dùng hàm isset để kiểm tra xem biến $_FILES["file_upload"]["error"] có tồn tại hay không.
preg_split: tách một chuỗi thành từng phần nhỏ theo regular expression. Trong ví dụ của bài viết, ta dùng hàm này để tách tên file cùng đường dẫn ra thành từng phần nhỏ (phân cách nhau bằng ký tự \ hoặc /, ta không biết chắc được client là Windows hay Linux nên ta tách theo trường hợp tổng quát). Sau khi tách, phần tử cuối cùng sẽ là tên file.
Một cách khác để lấy tên file là dùng hàm basename. Tuy nhiên sử dụng hàm này sẽ có một số vấn đề nảy sinh, bạn tham khảo thêm ở đây: http://www.php.net/manual/en/function.basename.php.
count: đếm số lượng phần tử trong mảng. $a[count($a)-1] sẽ truy cập tới phần tử cuối cùng của mảng $a.
preg_match: sử dụng regular expression để tìm xem chuỗi con có xuất hiện trong chuỗi mẹ hay không. Trong ví dụ của bài viết, ta dùng hàm này để kiểm tra xem tên của của có được kết thúc bằng .gif hoặc .jpg hay không.

move_uploaded_file: di chuyển file được upload từ client đến 1 thư mục khác trên server.


Comments