課程110:函式的應用
摘要:
在課程108撰寫資料庫網頁程式中,在各個網頁程式裡使用了大量相同的程式碼。程式開發者,在開發應用程式的過程當中,如果有碰到《似曾相識》的感覺時,就必須要有重新調整程式結構的警覺。將類似的程式碼,從原本的程式碼中抽離出來,按照功能,獨立成為一個功能模組,避免重複撰寫相同功能的程式碼。模組化的方式有兩種:一、自訂函式;二、物件導向。在這個課程我們先討論如何撰寫自訂的函式,讓程式模組化。然後,改寫原先的資料庫網頁程式。在下個課程中,我們將討論物件導向程式設計。

函式

說明:

函式,是一個獨立的程式碼區段,只需撰寫一次,就可以讓程式的其他部分,藉由呼叫函式的方式,重複使用。通常,函式會接受參數,執行特定的功能,傳回程式執行的結果。藉由使用函式,可以節省你的程式開發時間,以及減少程式的臭蟲,提高程式的可維護性。

但是,光是把程式碼,寫成函式,並不會讓你的程式變得更好,或是讓你的工作變得更輕鬆。你必須學習養成如何撰寫好的函式。好的函式,通常有以下的特質:

  1. 最好不超過一頁:太長的函式,通常不好維護,而且容易發生錯誤。
  2. 函式只執行一個特定的功能:如果,在一個函式裡面,執行多個功能時,通常必須把各功能,再切割成獨立的函式。這樣,才可以更模組化,在主程式組合這些功能時,能更有彈性。
  3. 好的函式,會有好名字:如果,你無法替你的函式,找出一個好名字的話,通常代表,你對函式的功能,沒有具體的認知。自然無法寫出好的函式。

在這一節中,我們使用範例,來學習如何撰寫函式:

  1. 函式的語法。
  2. 參數傳遞。
  3. 預設參數。
  4. 函式內變數的範圍。
  5. 使用 include()函式,來建立自己的函式庫。
一、函式的語法

function 函式名稱([參數1, 參數2, ...參數n]){

程式碼

}

說明:

  1. 撰寫函式時,在 function保留字之後,接著是使用者自己命名的函式名稱。函式名稱之後,是由括弧所包含的是要交給函式處理的參數。視情況需要,函式可以接受任何數目的參數,包括不接受任何參數。之後,是由 { 和 }所包含的程式區段。
  2. 函式的執行:在主程式呼叫函式之後,自 { 開始由上而下的順序,一直執行到 }為止或碰到 return 的命令句時結束。
  3. return 可以用來結束函式的執行,return 後面如果有接任何運算式,則會將運算式所得的結果,傳回給主程式。
範例:無參數,無回傳值

程式碼:

<?php
function today(){       
     $ty = date("Y") - 1911;
     echo '民國 '.$ty.'年'.date("m月d日");
}


today();
?>
執行程式
範例:無參數,有回傳值

程式碼:

<?php
function today(){
     $ty = date("Y") - 1911;
     return'民國 '.$ty.'年'.date("m月d日");
}
echo today();
?>
執行程式
範例:有參數,有回傳值

程式碼:

<?php
function numberOptions($start, $end, $selected){
     $option = "";
     for($i = $start; $i <= $end; $i++){
          $list .= "<option value='$i'";
          if($i==$selected){
          $list .= " SELECTED";
          }
          $list .=">$i</option>";
     }
     return $list;
}
$yr = date('Y') - 1911;
$yr_start = $yr - 5;
$yr_end = $yr + 5;
$mn = date('m');
$dy = date('d');
$yr_option = numberOptions($yr_start, $yr_end, $yr);
$mn_option = numberOptions(1, 12, $mn);
$dy_option = numberOptions(1, 31, $dy);
?>
民國 <select name=year><?php echo $yr_option; ?></select>年
<select name=month><?php echo $mn_option; ?></select>月
<select name=day><?php echo $dy_option; ?></select>日

 

執行程式
二、參數傳遞

說明:

當其他部分的程式碼,呼叫函式,以變數當作參數,傳遞給函式時,實際上函式所接受到的是這個變數的值,所以,在函式中的程式碼,如果更改這個變數時,並不會影響到原本程式中的變數。這種參數的傳遞方式,稱之為值的傳遞。

另一種參數傳遞的方式,稱之為參照的傳遞。使用這種方式傳遞參數給函式處理時,當函式更改參數的值,原本程式的變數值也會跟著改變。使用參照傳遞方式,在函式的定義中,參數之前必須加上 &符號。其他程式,呼叫這個函式時,必須在這個參數使用變數,不可以是其他的運算式(如:常數、定數)。

範例:參照傳遞

程式碼:

<?php
function add_by_value($value, $increment){
     $value += $increment;
}
function add_by_reference(&$value, $increment){
     $value += $increment;
}
$n = 100;
$i = 50;
echo '原本 n 的值: '.$n.'<br />';
add_by_value($n, $i);
echo '值傳遞之後 n 的值: '.$n.'<br />';
add_by_reference($n, $i);
echo '參照傳遞之後 n 的值: '.$n.'<br />';
?>

 

執行程式
三、預設參數

說明:

函式使用預設參數時,呼叫函式的程式,可以不需要提供這個參數,而使用函式中所定義的參數預設值。

使用預設參數時,預設參數必須放在非預設參數的後面(右邊)。

範例:預設參數

程式碼:

<?php
function numberOptions($start, $end, $selected=3){
     $option = "";
     for($i = $start; $i <= $end; $i++){
          $list .= "<option value='$i'";
          if($i==$selected){
               $list .= " SELECTED";
          }
          $list .=">$i</option>";
     }
     return $list;
}
$number_options = numberOptions(1, 10);
?>
<select name=number><?php echo $number_options; ?></select>

 

執行程式
四、函式內變數的範圍

說明:

函式內所使用的變數,除非特別指定,均為區域變數。區域變數的範圍,只限定在定義這個變數的函式內。函式外即使有同名的變數,因為範圍不同,所以是另一個變數。兩者不會互相影響。

如果,在函式之內有特別指定變數範圍(在變數名稱之前,加上 global 保留字)。變數變成全域變數。

範例:區域變數及全域變數

程式碼:

<?php
function local_scope(){
     $x = 5;
}
function global_scope(){
     global $x;
     $x = 10;
}
$x = 0;
echo '原本 x 的值: '.$x.'<br />';
local_scope();
echo '呼叫 local_scope()函式之後, x 的值: '.$x.'<br />';
global_scope();
echo '呼叫 global_scope()函式之後, x 的值: '.$x.'<br />';
?>

執行程式
五、使用 include()函式,來建立自己的函式庫

說明:

到目前為止,所有的函式,都是寫在和主程式同一個網頁中,所以,只有這個主程式的網頁可以呼叫這些函式。

更好的做法,是把所有函式按照功能的類別,寫在同一個檔案。然後,在主程式中,使用 include() 或 include_once() 函式,將函式的檔案,含括在主程式中。透過含括檔案的方式,就可以把函式的功能,提供給其他的PHP網頁程式使用。

範例:使用含括檔案的方式

程式碼:

<?php
// 檔名: 110_07.php
function numberOptions($start, $end, $selected){
     $option = "";
     for($i = $start; $i <= $end; $i++){
          $list .= "<option value='$i'";
          if($i==$selected){
               $list .= " SELECTED";
          }
               $list .=">$i</option>";
     }
     return $list;
}
?>
<?php
// 檔名: 110_08.php
include("110_07.php");
$yr = date('Y') - 1911;
$yr_start = $yr - 5;
$yr_end = $yr + 5;
$mn = date('m');
$dy = date('d');
$yr_option = numberOptions($yr_start, $yr_end, $yr);
$mn_option = numberOptions(1, 12, $mn);
?>
民國 <select name=year><?php echo $yr_option; ?></select>年
<select name=month><?php echo $mn_option; ?></select>月
<select name=day><?php echo $dy_option; ?></select>日
執行程式
 

將函式應用在留言版的資料庫網頁

說明:

在這一節當中,我們將原本資料庫中的程式碼,按照功能,將程式碼自原本的程式中抽離,獨立成為個別的函式。

我們按照以下的步驟,來修改留言版的程式:

  1. 準備工作
  2. 產生設定檔:config.php
  3. 產生資料庫函式檔:db.php
  4. 產生留言版函式檔:guestbook.php
  5. 修改原本的程式: index.php, add.php, update.php, delete.php
一、準備工作

說明:

將之前109課程所做好的PHP程式,放在另一個新的目錄 guestbook110 之下。

複製檔案:
  1. 使用putty連線到伺服主機。
  2. 輸入以下的命令,進入 public_html 目錄:
    cd public_html
  3. 輸入以下的命令,產生新的目錄 guestbook110,並將 guestbook109 中所有檔案,
    複製到guestbook110:
    cp -r guestbook109 guestbook110
二、產生設定檔:config.php

說明:

在 config.php 中,我們定義一些有關網站設定的一些變數的資料,方便網站的管理者,修改程式的設定。

程式碼:

<?php
/*
檔名: config.php
*/
/* 定義連接資料庫的有關參數 */
$DB_USER = 'lib13';
$DB_PASSWORD = 'mypassword';
$DB_HOST = 'localhost';
$DB_DATABASE = 'lib13';
?>
三、產生資料庫函式檔:db.php

說明:

在 db.php 檔案中,我們定義資料庫的初始化函式 db_init() ,供網站中所有需要存取資料庫的 PHP 程式使用。任何有關存取這個資料庫的相關功能的函式,都可以在這個檔案中定義。

程式碼:

<?php
/* 
檔名: db.php
目的: 存放跟整個資料庫操作有關的各函式
*/
include_once("config.php");
function db_init(){
     //使用全域變數
     global $DB_HOST;
     global $DB_USER;
     global $DB_PASSWORD;
     global $DB_DATABASE;
     /* 連接資料庫 */
     $link = mysql_pconnect($DB_HOST, $DB_USER, $DB_PASSWORD) 
          or die('無法連接到資料庫:'.mysql_error());
     /* 選用資料庫 */
     mysql_select_db($DB_DATABASE)
          or die('無法選擇資料庫['.$DB_DATABASE.']:'.mysql_error());
}
?>
四、產生留言版函式檔:guestbook.php

說明:

guestbook.php 這個檔案,定義了所有跟留言版有關的資料庫操作功能的函式。其他程式要存取有關留言版的資料,只要呼叫這個檔案中的函式即可。

程式碼:

<?php
/* 
檔名: guestbook.php
目的: 存放跟操作留言版各類功能有關的函式
*/
include_once("db.php");
// 呼叫 db.php 檔案中的 db_init()函式來初始化資料庫
db_init();
function getRecordset(){
     // 宣告一個陣列變數 $recordset, 存放查詢所得的紀錄集
     $recordset = array();
     // 定義 SQL 命令的字串變數
     $sql = 'SELECT * FROM Guestbook ORDER BY post_time DESC';
     // 傳回查詢結果
     $result = mysql_query($sql);
     if(!$result) die ('無法執行查詢: '.$sql);
     while($record = mysql_fetch_assoc($result)){
          array_push($recordset, $record);
     }
     return $recordset;
}
function Add(){
     // 定義 SQL 命令的字串變數
     $sql = "INSERT INTO Guestbook(name, email, web, content, post_time)";
     $sql .= " VALUES('$_POST[name]', '$_POST[email]', '$_POST[web]', ";
     $sql .= "'$_POST[content]', NOW())";
     // 執行查詢
     return mysql_query($sql);
}
function Retrieve(){
     // 定義 SQL 命令的字串變數
     $sql = 'SELECT * FROM Guestbook WHERE id='.$_GET[id];


     // 執行查詢
     $result = mysql_query($sql);
     if(!$result) die('執行查詢錯誤: '.$sql);
     return mysql_fetch_assoc($result);
}
function Update(){
     // 定義 SQL 命令的字串變數
     $sql = "UPDATE Guestbook SET name='$_POST[name]', email='$_POST[email]', ";
     $sql.= "web='$_POST[web]', content='$_POST[content]' WHERE id=$_GET[id]";
     // 執行查詢
     return mysql_query($sql);
}
function Delete(){
     // 定義 SQL 命令的字串變數
     $sql = "DELETE FROM Guestbook WHERE id=".$_GET[id];
     // 執行查詢
     return mysql_query($sql);
}
?>
五、修改原本的程式: index.php, add.php, update.php, delete.php

說明:

在這裡,我們使用含括 guestbook.php檔案,並拿掉所有跟資料庫有關的程式碼。藉由呼叫 guestbook.php 當中所定義的函式,執行各網頁程式所需要的功能。下面分別是修改後的各個程式的原始碼:

index.php
add.php
update.php
delete.php
執行程式