2.3 - 第二章:函數 - 變量作用域

PHP中存在三個變量作用域:全局變量、函數局部變量和類成員變量。全局變量,人如其名,可以在程序任何部分使用;在函數和類以外定義的變量就稱爲全局變量。

作者提示:類成員變量將在面向對象開發章節中詳細介紹。

在一個函數中,就進入了PHP的一個新的變量作用域——這裏的變量與全局變量是互不關聯,不會相互覆蓋。任何定義在函數內部的變量都會在函數執行完後消失。因此函數中變量名稱可以隨意定義而不需要擔心覆蓋外面的變量(也就是兼容性問題)。

$a = "Hello World";

function hello()
{
  $a = "Hello Reader";
  $b = "How are you";
}

hello();

echo $a; // 輸出"Hello World"
echo $b; // 發出一個“使用了不存在的變量”的警告

兩條途徑可以使全局變量作用於函數內;第一種方法是使用global關鍵字在函數內申明:

$a = "Hello";
$b = "World";

function hello()
{
  global $a, $b;
  echo "{$a} {$b}";
}

hello(); // 輸出"Hello World"

注意一點,使用global關鍵字,其後的變量使用逗號分隔,可以同時將多個全局變量作用到函數中——當然,你也可以使用多條global語句聲名,達到同樣的效果。

很多開發者感覺使用global關鍵字時全局變量與函數局部變量互通會引發一些問題。他們使用超全局數組$GLOBALS來解決這個問題:

$a = "Hello";
$b = "World";

function hello()
{
  echo $GLOBAL['a'] . ' ' . $GLOBAL['b']; 
}

hello(); // 輸出"Hello World"

2.3.1 參數傳遞

傳參機制允許向函數注入任意個數的參數來決定一個函數的運行結果:

function hello($who)
{
  echo "Hello {$who}";
}

hello("World");
/*
這裏我們向函數中傳入了一個"World"值,使得輸出結果是"Hello World"
*/

你可以在函數中定義任意多個參數,或者調用函數時傳入任意多個參數值,PHP都是可以接受的,但如果調用時傳入的參數少於定義在函數中的參數個數時,就會發生錯誤。

爲此我們需要給參數設定默認值。可選參數(調用函數是可以不傳這個參數)必須放在定義函數參數列表的最右側,請看以下示例:

function hello($who = "World")
{
  echo "Hello {$who}";
}

hello();
/* 這次我們調用函數是沒有傳入參數,輸出結果是默認的"Hello World" */


2.3.2 可變個數傳參

我們可能經常犯這樣的錯誤:

function f ($optional = "null", $required)
{

}

看似不會引起什麼問題,但這樣做也沒有什麼意義——因爲這樣做並不能使得第一個參數是可選參數,由於第二個參數是必傳參數,如果在調用函數時只傳入一個參數那麼只會引發一個警告,告訴你必須傳入兩個參數。

在此例中可能你需要可變個數傳參機制——這種機制可以讓你根據實際情況向一個函數傳入任意個數的參數。應用此種機制最經典的案例莫過於printf()系列函數。

針對此種機制PHP提供個內建函數:func_num_args()、func_get_arg() and func_get_args()。請看以下範例:

function hello()
{
  if (func_num_args() > 0) {
    $arg = func_get_arg(0); // 第一個參數位於0元素中
    echo "Hello {$arg}";
  } eles {
    echo "Hello World";
  }
}

hello("Siemen"); // 顯示 "Hello Siemen"
hello(); // 顯示 "Hello World"

在函數聲明中即使定義了參數變量仍然可以使用這種機制,他們之間不會相互影響——比如調用func_num_arg()可以獲得實際傳入參數的個數,包括函數中預定參數。

function countAll($arg1)
{
  if (func_num_args() == 0) {
    die("你需要至少傳入一個參數");
  } else {
    $args = func_get_args(); // 返回參數數組

    // 從參數數組中刪除第一個預定義元素
    array_shift($args);

    $count = strlen($arg1);

    foreach ($args as $arg) {
      $count += strlen($arg);
    }
  }

  return $count;
}

echo countAll("foo", "bar", "baz"); // 顯示結果 '9'

作者提示:請重點記住,使用可變個數傳參機制可能會導致一些意想不到的問題;這是一把雙刃劍,有數不勝數的情況來調用這個函數,使得代碼變得難以閱讀。

2.3.3 引用傳參

與函數參數以傳值方式相反的是,他們可以被引用的方式傳入,在之前我們學習引用操作符的時候我們已經知道了&符號的用處。它同樣可以作用在函數傳參上:

function countAll(&$count)
{
  if (func_num_args() == 0) {
    die("請至少傳入一個參數");
  } else {
    $args = func_get_args(); // 返回一個參數數組

    // 刪除預定義的第一個參數
    array_shift($args);

    foreach ($args as $arg) {
      $count += strlen($arg);
    }
  }
}

$count = 0;

countAll($count, "foo", "bar", "baz"); // 經過函數調用後$count變量現在的值是9

作者提示:與之前提醒的一樣,引用參數只能接受變量傳入,禁止傳入表達式(直接量)。

對於聲名函數引用參數默認值來說,PHP4是無法做到的,這是PHP5的新特性:

function cmdExists($cmd, &$output = null) {
  $output = 'whereis $cmd';
  if (strpos($output, DIRECTORY_SEPARATOR) !== FALSE) {
    return true;
  } else {
    return false;
  }
}

上例中,$output變量是可選參數——如果調用時沒有傳入值,那麼在cmdExists方法中將會創建一個同名局部變量。當然,函數執行返回值後這個變量會被銷燬。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章