hn4u @ Last updated 21/11/04 22:42
Go to my homepage at http://4u.jcisio.com
Full version available at http://4u.jcisio.com/r/article613.htm

Hải Nam

Phần 1: 7 lỗi giáo khoa

21. Sử dụng printf() không thích hợp

Hàm printf() dùng để in dữ liệu có định dạng

Nó có thể được dùng, thí dụ, khi bạn một in một số kiểu double với 2 số lẻ, hoặc trong bất kì tình huống nào bạn muốn thay đổi định dạng trước khi in.

Thí dụ dưới đây minh hoạ cách dùng đúng của printf(): định dạng số Pi với độ chính xác theo ý muốn

Mã lệnh (PHP)
<?
/*
* The three faces of &#928;
*/

printf ("Pi is:      %.2f\n<br>\n", M_PI);
printf ("Pi is also: %.3f\n<br>\n", M_PI);
printf ("Pi is also: %.4f\n<br>\n", M_PI);
?>

Chú ý. Tôi đã từng gặp những người sợ dùng printf(), thay vào đó lại dùng những hàm định dạng tự viết, dài đến 30-40 dòng, trong đi một câu printf() có thể làm mọi thứ anh ta mong muốn.

Nhiếu lập trình viên dùng sai printf(): in các biến, các giá trị trả về của hàm hoặc thỉnh thoảng, chỉ là dữ liệu thông thường. Thường xảy ra trong hai tình huống:

Khi nào print() thích hợp hơn?

Các lập trình viên thường sử dụng printf() trong khi chỉ print() là đủ. Xét thí dụ sau:

Mã lệnh (PHP)
<?
$name    = 'Sterling Hughes';
$job    = 'Senior Engineer';
$company    = 'DesignMultimedia';
$email    = 'shughes@designmultimedia.com';

printf ("My name is: %s\n<br>\n
    My Job is: %s, %s\n<br>\n
    My E-mail is: %s\n<br>\n"
,
   
$name, $job, $company, $email);
?>

Hàm print() có thể dùng thay cho printf() như sau:

Mã lệnh (PHP)
print "My Name is: $name\n<br>\n
    My Job is: $job, $company
\n<br>\n
    My E-mail is: $email
\n<br>\n";

Khi không cần định dạng sử liệu, dùng print() thay cho printf() có những lợi ích sau:

Dùng printf() để xuất dữ liệu trả về từ gọi hàm

Một lỗi thường gặp khác là dùn printf() để xuất dữ liệu trả về từ gọi hàm, thí dụ như hàm đếm dưới đây:

Mã lệnh (PHP)
<?
printf ("%d occurrences of %s found.",
   
count($result), $search_term);
?>

Khi xuất giá trị do hàm trả về, toán tử . nên dùng để nối trong print(), như dưới đây:

Mã lệnh (PHP)
<?
print count($result) .
      
" occurrences of $search_term found.";
?>

Dùng toán tử . nhanh hơn việc dùng printf()

20. Áp dụng sai ngữ nghĩa (semantics)

Nhiều lập trình viên sử dụng PHP mà không biết đến những điểm tinh tế của ngôn ngữ này. Một trong những điểm đó là sự khác nhau giữa cú pháp (syntax) và ngữ nghĩa (semantics).

Trong một ngôn ngữ lỏng lẻo như PHP, bạn có nhiều lựa chọn để viết lệnh. Các biến không cần có kiểu xác định...

Thí dụ sau mở tập tin và in từng dòng:

Mã lệnh (PHP)
<?
$fp = @fopen ('somefile.txt', 'r')
    or die (
'Cannot open somefile.txt');
while (
$line = @fgets ("$fp", 1024)) // error
{
    print
$line;
}

@
fclose ("$fp") // error
   
or die ('Cannot close somefile.txt');
?>

Thí dụ trên sẽ tạo lỗi

Warning: Supplied argument is not a valid File-Handle resource in C:\Inetpub\wwwroot\tst.php on line 4.

Đó là do biến $fp đặt trong dấu nháy kép nên được chuyển thành chuỗi. Thế mà hàm fopen() nhận một định danh tài nguyên (resource identifier) trong tham số đầu của nó, chứ không nhận một chuỗi. Để giải quyết vấn đề, bạn chỉ đơn giản bỏ dấu nháy kép đi

Mã lệnh (PHP)
<?
$fp = @fopen ('somefile.txt', 'r')
    or die (
'Cannot open somefile.txt');

while (
$line = @fgets ($fp, 1024))
{
    print
$line;
}

@
fclose ($fp)
    or die (
'Cannot close somefile.txt');
?>

Có thể tránh việc áp dụng sai ngữ nghĩa?

Our example above generated an error statement. But PHP enables you to customize your scripts to fit a unique scenario or output requirement. So, it is at least theoretically possible to "get away" with misapplying a semantic. Tôi không hiểu, nhưng dịch thế này được không?

Thí dụ trên của chúng ta tạo ra một thông báo lỗi. Nhưng PHP cho phép bạn tuỳ biến các script để thích hợp với một kịch bản khác thường hoặc với các đòi hỏi của thông tin ra. Do đó, ít nhất trên lí thuyết, bạn có khả năng tránh việc áp dụng sai ngữ nghĩa.

Vậy, bạn cần biết những hậu quả có thể có (possible outcomes) nếu bạn quyết định học về ngữ nghĩa. Áp dụng sai dẫn đến những lỗi khá tinh vi nếu bạn không chú ý.

Nếu bạn muốn tuỳ biến script, bạn cần hiểu những chủ đề chính sau:

19. Thiếu ghi chú

Theo ý tôi, mã nguồn thiếu ghi chú là căn nguyên của sự lập trình ích kỉ. Nó dẫn tới những hiệu chỉnh sai lầm, hiểu sai ý nghĩa và làm người đọc mệt mỏi. Nói chung, lập trình ghi chú (inline documentation) được mọi người khẳng nhận là điều tốt, nhưng hiếm khi nó tồn tại.

Một vấn đề khác là quá nhiều ghi chú. Dù hiếm gặp, nhưng nó làm cho các đoạn mã bị cắt vụn, gây ra sự khó theo dõi. Dưới đây là một thí dụ:

Mã lệnh (PHP)
<?
// Start off PHP code
$age = 18; // assign 18 to age
$age++; // increment $age by one

// print out introductory text
print "You are now 19, which means you have been:";
print
"\n<br>\n<br>\n";

// A for loop to print out all of the
// previous ages
for ($idx = 0; $idx < $age; $idx++)
{
   
// Print out an individual age
   
print "$idx years old\n<br>\n";
}
// End the PHP code
?>

Bao nhiêu ghi chú thì đủ?

Nhiếu đến mức nào, điều đó tuỳ thuộc ngân sách của bạn, vào chính sách của công ty và vào độ phức tạp của chương trình. Tuy nhiên, cũng có một vài gợi ý cho bạn

Dưới đây là một thí dụ về ghi chú tốt

Mã lệnh (PHP)
<?
// Random_Numbers.lib
//   Generate different types of random numbers.

mt_srand((double)microtime()*1000000);

//
// mixed random_element(array elements[, array weights])
//   Extract a random element from elements.  Weights is
//   the relative probability that each element will be
//   selected.
//

function random_element ($elements, $weights=array())
{

// There must be exactly the same amount of elements as
// there are weights for this algorithm to work properly

if (count ($weights) == count ($elements)) {
    foreach (
$elements as $element)
    {
    foreach (
$weights as $idx)
    {
       
// Note:  we don't use $idx, since we
        // don't want to override elements.
       
$randomAr[] = $element;
    }
    }
} else {
   
$randomAr = $elements;
}

$random_element = mt_rand (0, count ($randomAr)-1);
return
$randomAr[ $random_element ];
}
?>

18. Nhiều biến, tốn nhiều thời gian

Có vài người bị ám ảnh bởi biến trung gian. Tôi không thể hiểu nổi tại sao ai đó có thể viết như thế này:

Mã lệnh (PHP)
<?
$tmp = date ("F d, h:i a"); /* ie January 3, 2:30 pm */
print $tmp;
?>

Tại sao phải dùng biến trung gian? Nó không cần thiết

Mã lệnh (PHP)
<?
print date ("F d, h:i a");
?>

Rủi thay, có vẻ như rất nhiều người khó bỏ được thói quen xấu này.

Biến tạm làm chậm thời gian thi hành chương tình của bạn. Tốt hơn là nên bỏ qua đó và gộp các lời gọi hàm với nhau. Những người dùng biến tạm thường làm chương của họ chạy chậm đến 25%.

Một lí do khác để tránh có quá nhiều biến tạm là vì trông nó không được đẹp mắt. Trong hai thí dụ trên, thí dụ nào súc tích hơn? Thí dụ nào làm con mắt dễ chịu hơn? Dùng quá nhiều biến tạm có thể dẫn đến mã chương trình khó đọc và không súc tích.

Lợi điểm của dùng biến tạm

Các biến tạm có lợi trong việc thay thế các hàm hay biểu thức dài lê thê. Nó có vai trò như bí danh giả. Điều này đặc biệt đúng khi bạn dùng một hàm hay biểu thức nhiều lần.

Xem xét thí dụ đây, nó không dùng nhiều biến hơn mức tối thiểu

Mã lệnh (PHP)
// string reverse_characters(string str)
//   Reverse all of the characters in a string.
function reverse_characters ($str)
{
    return
implode ("", array_reverse (preg_split("//", $str)));
}

Nội dung trong hàm implode() dài và do đó khó đọc. Dùng một hoặc nhiều biến tạm có thể giúp chúng ta:

Mã lệnh (PHP)
// string reverse_characters(string str)
//   Reverse all of the characters in a string.
function reverse_characters ($str)
{
   
$characters = preg_split ("//", $str);
   
$characters = array_reverse ($characters);

    return
implode ("", $characters);
}

Các luật chung của ngón tay cái

Khi quyết định có dùng biến tạm hoặc không, bạn nên suy nghĩ về 2 câu hỏi:

Nếu ít nhất một câu trả lời là có, thì nên dùng biến tạm. Còn không, vứt nó đi và tổ hợp các hàm lại (nếu cần).

17. Viết lại các hàm có sẵn

Một số nơi phổ biến mã nguồn các script PHP chủ trương đổi tên các hàm sẵn có để tạo sự dễ dàng cho các lập trình viên chuyển từ VB sang. Thí dụ:

Mã lệnh (PHP)
<?
function len ($str)
{
    return
strlen ($str);
}
?>

Lại có một số người cố gắng viết lại các hàm PHP thông dụng thay vì đi học về hàm đó trong các tài liệu PHP cung cấp.

Có ít nhất 2 lí do để không nên làm điều này. Thứ nhất, và trên nhất, nó làm cho những người đọc (và sửa) chương trình của bạn khó hiểu và cảm thấy có quá nhiều hàm dư thừa. Họ tự hỏi tại sao bạn lại đi định nghĩa hàm theo kiểu đó, thay vì sử dụng các hàm định nghĩa sẵn bởi PHP.

Thứ hai, định nghĩa hàm như vậy cũng sẽ làm chậm chương trình của bạn (một cách không cần thiết). Không chỉ phải xử lí nhiều mã hơn, mà mỗi lần gọi hàm do bạn định nghĩa, bạn đã tốn thời gian cho chính hàm đó, trước khi hàm nguyên thuỷ được gọi.

Tránh viết lại các hàm có sẵn

Hãy đương đầu với nó. Đôi khi thật là khó để tránh chuyện này. Trước tiên, một lập trình viên không thể theo kịp các hàm của PHP ngay được. Và ai có thời gian mà tra cứu. Tại sao không viết lại cho khoẻ?

Cách làm của tôi là luôn có sẵn một tài liệu chỉ dẫn PHP (PHP manual) mỗi khi viết chương trình (tác giả bài này dùng một bản PDF có tạo chỉ mục, riêng tôi, người dịch, thì dùng một tài liệu CHM đầy đủ thông tin và có cả góp ý của người sử dụng mà bạn có thể lấy ở http://www.php.net/docs.php). Sau đó, mỗi khi định viết một hàm mở rộng cho PHP, tôi đọc lướt qua tài liệu để xem hàm đó có chưa.

Tuy nhiên, cần chú ý là, do bản chất mã nguồn mở của PHP, bạn có thể tìm được các hàm do người dùng định nghĩa trước khi nó được thêm vào PHP (thí dụ như hàm tìm phần tử khác nhau giữa hai mảng). Điều này không có nghĩa là bạn phải hiệu chỉnh lại mã (This doesn't necessarily mean that you should have to correct the code. - don't understand)

16. Không tách biệt phần server và client

Vài lập trình viên cố kết nối cả chương trình với nhau, nghĩa là ghép chung mã HTML (client-side - phần khách) với mã PHP (server-side - phần chủ) vào trong một tập tin lớn.

Mặc dù điều này tốt cho các site nhỏ, nhưng nó có thể trở thành vấn đề lớn khi các site đó trở nên lớn hơn và được bổ sung thêm tính năng. Lập trình theo cách này làm nảy sinh vấn đề khó bảo trì và các tập tin trở nên cồng kềnh.

Hàm API

Khi muốn tách biệt phần khách - chủ, bạn có vài lựa chọn. Một cách là viết những hàm hiển thị nội dung linh động và đặt chúng đúng chỗ trong trang web.

Thí dụ dưới đây minh hoạ điều này:

index.php - phần khách

HTML
<?php include_once ("site.lib"); ?>
<html>
<head>
   <title> <?php print_header (); ?> </title>
</head>
<body>
<h1> <?php print_header (); ?> </h1>
<table border="0" cellpadding="0" cellspacing="0">
   <tr>
       <td width="25%">
           <?php print_links (); ?>
       </td>
       <td>
           <?php print_body (); ?>
       </td>
</tr>
</table>
</body>
</html>

site.lib - phần chủ

Mã lệnh (PHP)
<?php
$dbh = mysql_connect ("localhost", "sh", "pass")
  or die (
sprintf ("Cannot connect to MySQL [%s]: %s", mysql_errno (), mysql_error ()));
@
mysql_select_db ("MainSite")
  or die (
sprintf ("Cannot select database [%s]: %s", mysql_errno (), mysql_error ()));

$sth = @mysql_query ("SELECT * FROM site", $dbh)
  or die (
sprintf ("Cannot execute query [%s]: %s", mysql_errno (), mysql_error ()));

$site_info = mysql_fetch_object ($sth);

function
print_header ()
{
    global
$site_info;
    print
$site_info->header;
}

function
print_body ()
{
    global
$site_info;
    print
nl2br ($site_info->body);
}

function
print_links ()
{
    global
$site_info;

   
$links = explode ("\n", $site_info->links);
   
$names = explode ("\n", $site_info->link_names);

    for (
$i = 0; $i < count ($links); $i++)
    {
        print
"\t\t\t <a href=\"$links[$i]\">$names[$i]</a> \n<br>\n";
    }
}
?>

Như bạn thấy trong thí dụ trên, tách biệt khách chủ làm tăng tính dễ đọc trong chương trình của bạn. Một lợi ích khác là một khi bạn đã có các hàm API hiển thị nội dung, bạn có thể để cho thiết kế viên tham gia thay đổi bố cục mà không cần sửa mã chương trình.

Lợi ích của hàm API

Bất lợi

Hệ thống khuôn mẫu

Một cách khác để tách biệt khách chủ là dùng hệ thống khuôn mẫu. Nghĩa là, có một số đánh dấu nội dung sau đó dùng chương trình phân tích, thay thế các đánh dấu đó bằng thông tin cần thiết.

Thí dụ, bạn có thể tạo một tập tin như thế này:

HTML
<html>
<head>
   <title>%%PAGE_TITLE%%</title>
</head>
<body %%BODY_PROPERTIES%%>
<h1>%%PAGE_TITLE%%</h1>
<table border="0" cellpadding="0" cellspacing="0">
   <tr>
       <td width="25%">%%PAGE_LINKS%%</td>
       <td>%%PAGE_CONTENT%%</td>
</tr>
</table>
</body>
</html>

Sau đó có thể viết chương trình phân tách tập tin, thay thế các thông tin trong dấu cách %% bằng các thông tin thích hợp.

Ghi chú: một lớp hỗ trợ hệ thống khuôn mẫu khá tốt là lớp FastTemplate, có ở www.thewebmasters.net

Ưu điểm của hệ thống khuôn mẫu

Nhược điểm

15. Dùng các cấu trúc lỗi thời

Có nhiều người cứ dùng mãi các mã và thư viện lỗi thời. Thí dụ như họ đã viết một hàm dùng ở PHP 2, và vẫn còn dùng nó ở PHP 4, mặc dù một hàm có cùng mục đích như thế đã được thêm vào ở PHP 3

Dùng các cấu trúc lỗi thời có thể làm chậm chương trình của bạn, cũng như làm cho nó trở nên khó hiểu. Người đọc các chương trình của bạn có thể không quen với các hàm lỗi thời của PHP. Tuy nhiên, khi phát hiện một đoạn mã lạc hậu, bạn đừng nghĩ rằng cần phải thay thế nó. Chỉ cần chắc chắn rằng bạn sẽ không dùng nó cho các chương trình viết trong tương lai.

Một thí dụ về cấu trúc lỗi thời, mà nhiều người có vẻ cố nắm lấy, là cú pháp beginControlStructure .. endControlStructure;

Mã lệnh (PHP)
<?
// Bad/Outdated Practice
while (1):
    print
"5";
    if (
$idx++ == 5):
        break;
    endif;
endwhile;

// Better Practice
// (the code could be optimized though)
while (1)
{
    print
"5";
    if (
$idx++ == 5) {
        break;
    }
}
?>

Đây là một thói quen xấu vì

Ở trên chỉ là một thí dụ về cấu trúc lỗi thời. Nó còn nhiều nữa. Như một quy tắc, bạn nên theo những các viết trong tài liệu PHP. Hầu hết nó được cập nhật mới. Nó cũng dùng các hàm mới nhất của PHP trong thí dụ của mình. Nên thường xuyên kiểm tra tài liệu khi bạn có ý muốn mở rộng tính năng nào đó của PHP. Theo cách này, bạn sẽ không phải viết lại các hàm có sẵn.

Tổng kết

Trong bài này bạn đã đi qua 7 trên tổng số 21 lỗi mà lập trình viên PHP mắc phải. Những lỗi giáo khoa này bao gồm:


hainam4u @ Last updated 21/11/04 22:42
Go to my homepage at http://4u.jcisio.com